Branch data Line data Source code
1 : : /********************************************************************
2 : : * gnc-schedxactions-xml-v2.c -- xml routines for transactions *
3 : : * Copyright (C) 2001,2007 Joshua Sled <jsled@asynchronous.org> *
4 : : * *
5 : : * This program is free software; you can redistribute it and/or *
6 : : * modify it under the terms of the GNU General Public License as *
7 : : * published by the Free Software Foundation; either version 2 of *
8 : : * the License, or (at your option) any later version. *
9 : : * *
10 : : * This program is distributed in the hope that it will be useful, *
11 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 : : * GNU General Public License for more details. *
14 : : * *
15 : : * You should have received a copy of the GNU General Public License*
16 : : * along with this program; if not, contact: *
17 : : * *
18 : : * Free Software Foundation Voice: +1-617-542-5942 *
19 : : * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
20 : : * Boston, MA 02110-1301, USA gnu@gnu.org *
21 : : * *
22 : : *******************************************************************/
23 : : #include <glib.h>
24 : :
25 : : #include <config.h>
26 : : #include <string.h>
27 : :
28 : : #include "SX-book.h"
29 : :
30 : : #include "gnc-xml-helper.h"
31 : : #include "sixtp.h"
32 : : #include "sixtp-utils.h"
33 : : #include "sixtp-parsers.h"
34 : : #include "sixtp-utils.h"
35 : : #include "sixtp-dom-parsers.h"
36 : : #include "sixtp-dom-generators.h"
37 : :
38 : : #include "gnc-xml.h"
39 : :
40 : : #include "io-gncxml-v2.h"
41 : : #include "io-gncxml-gen.h"
42 : :
43 : : #include "sixtp-dom-parsers.h"
44 : :
45 : : #undef G_LOG_DOMAIN
46 : : #define G_LOG_DOMAIN "gnc.backend.file.sx"
47 : : static const QofLogModule log_module = G_LOG_DOMAIN;
48 : :
49 : : #define SX_ID "sx:id"
50 : : #define SX_NAME "sx:name"
51 : : #define SX_ENABLED "sx:enabled"
52 : : #define SX_AUTOCREATE "sx:autoCreate"
53 : : #define SX_AUTOCREATE_NOTIFY "sx:autoCreateNotify"
54 : : #define SX_ADVANCE_CREATE_DAYS "sx:advanceCreateDays"
55 : : #define SX_ADVANCE_REMIND_DAYS "sx:advanceRemindDays"
56 : : #define SX_INSTANCE_COUNT "sx:instanceCount"
57 : : #define SX_START "sx:start"
58 : : #define SX_LAST "sx:last"
59 : : #define SX_NUM_OCCUR "sx:num-occur"
60 : : #define SX_REM_OCCUR "sx:rem-occur"
61 : : #define SX_END "sx:end"
62 : : #define SX_TEMPL_ACCT "sx:templ-acct"
63 : : #define SX_FREQSPEC "sx:freqspec"
64 : : #define SX_SCHEDULE "sx:schedule"
65 : : #define SX_SLOTS "sx:slots"
66 : : #define SX_DEFER_INSTANCE "sx:deferredInstance"
67 : :
68 : : /*
69 : : * FIXME: These should be defined in a header somewhere
70 : : */
71 : :
72 : : #define GNC_ACCOUNT_TAG "gnc:account"
73 : : #define GNC_TRANSACTION_TAG "gnc:transaction"
74 : : #define GNC_SCHEDXACTION_TAG "gnc:schedxaction"
75 : :
76 : : const gchar* schedxaction_version_string = "1.0.0";
77 : : const gchar* schedxaction_version2_string = "2.0.0";
78 : :
79 : : xmlNodePtr
80 : 2 : gnc_schedXaction_dom_tree_create (SchedXaction* sx)
81 : : {
82 : : xmlNodePtr ret;
83 : : const GDate* date;
84 : : gint instCount;
85 : : const GncGUID* templ_acc_guid;
86 : 2 : gboolean allow_2_2_incompat = TRUE;
87 : 2 : gchar* name = g_strdup (xaccSchedXactionGetName (sx));
88 : :
89 : 2 : templ_acc_guid = xaccAccountGetGUID (sx->template_acct);
90 : :
91 : : /* FIXME: this should be the same as the def in io-gncxml-v2.c */
92 : 2 : ret = xmlNewNode (NULL, BAD_CAST GNC_SCHEDXACTION_TAG);
93 : :
94 : 2 : if (allow_2_2_incompat)
95 : 2 : xmlSetProp (ret, BAD_CAST "version", BAD_CAST schedxaction_version2_string);
96 : : else
97 : 0 : xmlSetProp (ret, BAD_CAST "version", BAD_CAST schedxaction_version_string);
98 : :
99 : 2 : xmlAddChild (ret,
100 : : guid_to_dom_tree (SX_ID,
101 : 2 : xaccSchedXactionGetGUID (sx)));
102 : :
103 : 2 : xmlNewTextChild (ret, NULL, BAD_CAST SX_NAME, checked_char_cast (name));
104 : 2 : g_free (name);
105 : :
106 : 2 : if (allow_2_2_incompat)
107 : : {
108 : 0 : xmlNewTextChild (ret, NULL, BAD_CAST SX_ENABLED,
109 : 2 : BAD_CAST (sx->enabled ? "y" : "n"));
110 : : }
111 : :
112 : 0 : xmlNewTextChild (ret, NULL, BAD_CAST SX_AUTOCREATE,
113 : 2 : BAD_CAST (sx->autoCreateOption ? "y" : "n"));
114 : 0 : xmlNewTextChild (ret, NULL, BAD_CAST SX_AUTOCREATE_NOTIFY,
115 : 2 : BAD_CAST (sx->autoCreateNotify ? "y" : "n"));
116 : 2 : xmlAddChild (ret, int_to_dom_tree (SX_ADVANCE_CREATE_DAYS,
117 : 2 : sx->advanceCreateDays));
118 : 2 : xmlAddChild (ret, int_to_dom_tree (SX_ADVANCE_REMIND_DAYS,
119 : 2 : sx->advanceRemindDays));
120 : :
121 : 2 : instCount = gnc_sx_get_instance_count (sx, NULL);
122 : 2 : xmlAddChild (ret, int_to_dom_tree (SX_INSTANCE_COUNT,
123 : : instCount));
124 : :
125 : 2 : xmlAddChild (ret,
126 : : gdate_to_dom_tree (SX_START,
127 : : xaccSchedXactionGetStartDate (sx)));
128 : :
129 : 2 : date = xaccSchedXactionGetLastOccurDate (sx);
130 : 2 : if (g_date_valid (date))
131 : : {
132 : 2 : xmlAddChild (ret, gdate_to_dom_tree (SX_LAST, date));
133 : : }
134 : :
135 : 2 : if (xaccSchedXactionHasOccurDef (sx))
136 : : {
137 : :
138 : 0 : xmlAddChild (ret, int_to_dom_tree (SX_NUM_OCCUR,
139 : 0 : xaccSchedXactionGetNumOccur (sx)));
140 : 0 : xmlAddChild (ret, int_to_dom_tree (SX_REM_OCCUR,
141 : 0 : xaccSchedXactionGetRemOccur (sx)));
142 : :
143 : : }
144 : 2 : else if (xaccSchedXactionHasEndDate (sx))
145 : : {
146 : 0 : xmlAddChild (ret,
147 : : gdate_to_dom_tree (SX_END,
148 : : xaccSchedXactionGetEndDate (sx)));
149 : : }
150 : :
151 : : /* output template account GncGUID */
152 : 2 : xmlAddChild (ret,
153 : : guid_to_dom_tree (SX_TEMPL_ACCT,
154 : : templ_acc_guid));
155 : :
156 : 2 : if (allow_2_2_incompat)
157 : : {
158 : 2 : xmlNodePtr schedule_node = xmlNewNode (NULL,
159 : : BAD_CAST "sx:schedule");
160 : 2 : GList* schedule = gnc_sx_get_schedule (sx);
161 : 4 : for (; schedule != NULL; schedule = schedule->next)
162 : : {
163 : 2 : xmlAddChild (schedule_node, recurrence_to_dom_tree ("gnc:recurrence",
164 : 2 : (Recurrence*)schedule->data));
165 : : }
166 : 2 : xmlAddChild (ret, schedule_node);
167 : : }
168 : :
169 : : /* Output deferred-instance list. */
170 : : {
171 : : xmlNodePtr instNode;
172 : : SXTmpStateData* tsd;
173 : : GList* l;
174 : :
175 : 2 : for (l = gnc_sx_get_defer_instances (sx); l; l = l->next)
176 : : {
177 : 0 : tsd = (SXTmpStateData*)l->data;
178 : :
179 : 0 : instNode = xmlNewNode (NULL, BAD_CAST SX_DEFER_INSTANCE);
180 : 0 : if (g_date_valid (&tsd->last_date))
181 : : {
182 : 0 : xmlAddChild (instNode, gdate_to_dom_tree (SX_LAST,
183 : 0 : &tsd->last_date));
184 : : }
185 : 0 : xmlAddChild (instNode, int_to_dom_tree (SX_REM_OCCUR,
186 : 0 : tsd->num_occur_rem));
187 : 0 : xmlAddChild (instNode, int_to_dom_tree (SX_INSTANCE_COUNT,
188 : 0 : tsd->num_inst));
189 : 0 : xmlAddChild (ret, instNode);
190 : : }
191 : : }
192 : :
193 : : /* xmlAddChild won't do anything with a NULL, so tests are superfluous. */
194 : 2 : xmlAddChild (ret, qof_instance_slots_to_dom_tree (SX_SLOTS,
195 : 2 : QOF_INSTANCE (sx)));
196 : 2 : return ret;
197 : : }
198 : :
199 : : struct sx_pdata
200 : : {
201 : : SchedXaction* sx;
202 : : QofBook* book;
203 : : gboolean saw_freqspec;
204 : : gboolean saw_recurrence;
205 : : };
206 : :
207 : : static
208 : : gboolean
209 : 5 : sx_id_handler (xmlNodePtr node, gpointer sx_pdata)
210 : : {
211 : 5 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
212 : 5 : SchedXaction* sx = pdata->sx;
213 : 5 : auto tmp = dom_tree_to_guid (node);
214 : :
215 : 5 : g_return_val_if_fail (tmp, FALSE);
216 : 5 : xaccSchedXactionSetGUID (sx, &*tmp);
217 : :
218 : 5 : return TRUE;
219 : : }
220 : :
221 : : static
222 : : gboolean
223 : 5 : sx_name_handler (xmlNodePtr node, gpointer sx_pdata)
224 : : {
225 : 5 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
226 : 5 : return apply_xmlnode_text (xaccSchedXactionSetName, pdata->sx, node);
227 : : }
228 : :
229 : : static gboolean
230 : 5 : sx_enabled_handler (xmlNodePtr node, gpointer sx_pdata)
231 : : {
232 : 5 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
233 : 5 : auto set_enabled = [](SchedXaction* sx, const char* txt)
234 : : {
235 : 5 : sx->enabled = !g_strcmp0 (txt, "y");
236 : 5 : };
237 : 10 : return apply_xmlnode_text (set_enabled, pdata->sx, node);
238 : : }
239 : :
240 : : static gboolean
241 : 5 : sx_autoCreate_handler (xmlNodePtr node, gpointer sx_pdata)
242 : : {
243 : 5 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
244 : 5 : auto set_autocreate = [](SchedXaction* sx, const char* txt)
245 : : {
246 : 5 : sx->autoCreateOption = !g_strcmp0 (txt, "y");
247 : 5 : };
248 : 10 : return apply_xmlnode_text (set_autocreate, pdata->sx, node);
249 : : }
250 : :
251 : : static gboolean
252 : 5 : sx_notify_handler (xmlNodePtr node, gpointer sx_pdata)
253 : : {
254 : 5 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
255 : 5 : auto set_notify = [](SchedXaction* sx, const char* txt)
256 : : {
257 : 5 : sx->autoCreateNotify = !g_strcmp0 (txt, "y");
258 : 5 : };
259 : 10 : return apply_xmlnode_text (set_notify, pdata->sx, node);
260 : : }
261 : :
262 : : static gboolean
263 : 5 : sx_advCreate_handler (xmlNodePtr node, gpointer sx_pdata)
264 : : {
265 : 5 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
266 : 5 : SchedXaction* sx = pdata->sx;
267 : : gint64 advCreate;
268 : :
269 : 5 : if (! dom_tree_to_integer (node, &advCreate))
270 : : {
271 : 0 : return FALSE;
272 : : }
273 : :
274 : 5 : xaccSchedXactionSetAdvanceCreation (sx, advCreate);
275 : 5 : return TRUE;
276 : : }
277 : :
278 : : static gboolean
279 : 5 : sx_advRemind_handler (xmlNodePtr node, gpointer sx_pdata)
280 : : {
281 : 5 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
282 : 5 : SchedXaction* sx = pdata->sx;
283 : : gint64 advRemind;
284 : :
285 : 5 : if (! dom_tree_to_integer (node, &advRemind))
286 : : {
287 : 0 : return FALSE;
288 : : }
289 : :
290 : 5 : xaccSchedXactionSetAdvanceReminder (sx, advRemind);
291 : 5 : return TRUE;
292 : : }
293 : :
294 : : static
295 : : gboolean
296 : 8 : sx_set_date (xmlNodePtr node, SchedXaction* sx,
297 : : void (*settor) (SchedXaction* sx, const GDate* d))
298 : : {
299 : : GDate* date;
300 : 8 : date = dom_tree_to_gdate (node);
301 : 8 : g_return_val_if_fail (date, FALSE);
302 : 8 : (*settor) (sx, date);
303 : 8 : g_date_free (date);
304 : :
305 : 8 : return TRUE;
306 : : }
307 : :
308 : : static
309 : : gboolean
310 : 5 : sx_instcount_handler (xmlNodePtr node, gpointer sx_pdata)
311 : : {
312 : 5 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
313 : 5 : SchedXaction* sx = pdata->sx;
314 : : gint64 instanceNum;
315 : :
316 : 5 : if (! dom_tree_to_integer (node, &instanceNum))
317 : : {
318 : 0 : return FALSE;
319 : : }
320 : :
321 : 5 : gnc_sx_set_instance_count (sx, instanceNum);
322 : 5 : return TRUE;
323 : : }
324 : :
325 : : static
326 : : gboolean
327 : 5 : sx_start_handler (xmlNodePtr node, gpointer sx_pdata)
328 : : {
329 : 5 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
330 : 5 : SchedXaction* sx = pdata->sx;
331 : :
332 : 5 : return sx_set_date (node, sx, xaccSchedXactionSetStartDate);
333 : : }
334 : :
335 : : static
336 : : gboolean
337 : 2 : sx_last_handler (xmlNodePtr node, gpointer sx_pdata)
338 : : {
339 : 2 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
340 : 2 : SchedXaction* sx = pdata->sx;
341 : :
342 : 2 : return sx_set_date (node, sx, xaccSchedXactionSetLastOccurDate);
343 : : }
344 : :
345 : : static
346 : : gboolean
347 : 1 : sx_end_handler (xmlNodePtr node, gpointer sx_pdata)
348 : : {
349 : 1 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
350 : 1 : SchedXaction* sx = pdata->sx;
351 : :
352 : 1 : return sx_set_date (node, sx, xaccSchedXactionSetEndDate);
353 : : }
354 : :
355 : : static void
356 : 0 : _fixup_recurrence_start_dates (const GDate* sx_start_date, GList* schedule)
357 : : {
358 : : GList* iter;
359 : 0 : for (iter = schedule; iter != NULL; iter = iter->next)
360 : : {
361 : : Recurrence* r;
362 : : GDate start, next;
363 : :
364 : 0 : r = (Recurrence*)iter->data;
365 : :
366 : 0 : start = *sx_start_date;
367 : 0 : g_date_subtract_days (&start, 1);
368 : :
369 : 0 : g_date_clear (&next, 1);
370 : :
371 : 0 : recurrenceNextInstance (r, &start, &next);
372 : 0 : g_return_if_fail (g_date_valid (&next));
373 : :
374 : : {
375 : : gchar date_str[128];
376 : : gchar* sched_str;
377 : :
378 : 0 : g_date_strftime (date_str, 127, "%x", &next);
379 : 0 : sched_str = recurrenceToString (r);
380 : 0 : DEBUG ("setting recurrence [%s] start date to [%s]",
381 : : sched_str, date_str);
382 : 0 : g_free (sched_str);
383 : : }
384 : :
385 : 0 : recurrenceSet (r,
386 : 0 : recurrenceGetMultiplier (r),
387 : : recurrenceGetPeriodType (r),
388 : : &next,
389 : : recurrenceGetWeekendAdjust (r));
390 : : }
391 : :
392 : 0 : if (g_list_length (schedule) == 1
393 : 0 : && recurrenceGetPeriodType ((Recurrence*)g_list_nth_data (schedule,
394 : : 0)) == PERIOD_ONCE)
395 : : {
396 : : char date_buf[128];
397 : 0 : Recurrence* fixup = (Recurrence*)g_list_nth_data (schedule, 0);
398 : 0 : g_date_strftime (date_buf, 127, "%x", sx_start_date);
399 : 0 : recurrenceSet (fixup, 1, PERIOD_ONCE, sx_start_date, WEEKEND_ADJ_NONE);
400 : 0 : DEBUG ("fixed up period=ONCE Recurrence to date [%s]", date_buf);
401 : : }
402 : : }
403 : :
404 : : static gboolean
405 : 0 : sx_freqspec_handler (xmlNodePtr node, gpointer sx_pdata)
406 : : {
407 : 0 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
408 : 0 : SchedXaction* sx = pdata->sx;
409 : : GList* schedule;
410 : : gchar* debug_str;
411 : :
412 : 0 : g_return_val_if_fail (node, FALSE);
413 : :
414 : 0 : schedule = dom_tree_freqSpec_to_recurrences (node, pdata->book);
415 : 0 : gnc_sx_set_schedule (sx, schedule);
416 : 0 : debug_str = recurrenceListToString (schedule);
417 : 0 : DEBUG ("parsed from freqspec [%s]", debug_str);
418 : 0 : g_free (debug_str);
419 : :
420 : 0 : _fixup_recurrence_start_dates (xaccSchedXactionGetStartDate (sx), schedule);
421 : 0 : pdata->saw_freqspec = TRUE;
422 : :
423 : 0 : return TRUE;
424 : : }
425 : :
426 : : static gboolean
427 : 5 : sx_schedule_recurrence_handler (xmlNodePtr node, gpointer parsing_data)
428 : : {
429 : 5 : GList** schedule = (GList**)parsing_data;
430 : : gchar* sched_str;
431 : 5 : Recurrence* r = dom_tree_to_recurrence (node);
432 : 5 : g_return_val_if_fail (r, FALSE);
433 : 5 : sched_str = recurrenceToString (r);
434 : 5 : DEBUG ("parsed recurrence [%s]", sched_str);
435 : 5 : g_free (sched_str);
436 : 5 : *schedule = g_list_append (*schedule, r);
437 : 5 : return TRUE;
438 : : }
439 : :
440 : : struct dom_tree_handler sx_recurrence_list_handlers[] =
441 : : {
442 : : { "gnc:recurrence", sx_schedule_recurrence_handler, 0, 0 },
443 : : { NULL, NULL, 0, 0 }
444 : : };
445 : :
446 : : static gboolean
447 : 5 : sx_recurrence_handler (xmlNodePtr node, gpointer _pdata)
448 : : {
449 : 5 : struct sx_pdata* parsing_data = static_cast<decltype (parsing_data)> (_pdata);
450 : 5 : GList* schedule = NULL;
451 : : gchar* debug_str;
452 : :
453 : 5 : g_return_val_if_fail (node, FALSE);
454 : :
455 : 5 : if (!dom_tree_generic_parse (node, sx_recurrence_list_handlers, &schedule))
456 : 0 : return FALSE;
457 : : // g_return_val_if_fail(schedule, FALSE);
458 : 5 : debug_str = recurrenceListToString (schedule);
459 : 5 : DEBUG ("setting freshly-parsed schedule: [%s]", debug_str);
460 : 5 : g_free (debug_str);
461 : 5 : gnc_sx_set_schedule (parsing_data->sx, schedule);
462 : 5 : parsing_data->saw_recurrence = TRUE;
463 : 5 : return TRUE;
464 : : }
465 : :
466 : : static
467 : : gboolean
468 : 0 : sx_defer_last_handler (xmlNodePtr node, gpointer gpTSD)
469 : : {
470 : : GDate* gd;
471 : 0 : SXTmpStateData* tsd = (SXTmpStateData*)gpTSD;
472 : :
473 : 0 : g_return_val_if_fail (node, FALSE);
474 : 0 : gd = dom_tree_to_gdate (node);
475 : 0 : g_return_val_if_fail (gd, FALSE);
476 : 0 : tsd->last_date = *gd;
477 : 0 : g_date_free (gd);
478 : 0 : return TRUE;
479 : : }
480 : :
481 : : static
482 : : gboolean
483 : 0 : sx_defer_rem_occur_handler (xmlNodePtr node, gpointer gpTSD)
484 : : {
485 : : gint64 remOccur;
486 : 0 : SXTmpStateData* tsd = (SXTmpStateData*)gpTSD;
487 : 0 : g_return_val_if_fail (node, FALSE);
488 : :
489 : 0 : if (! dom_tree_to_integer (node, &remOccur))
490 : : {
491 : 0 : return FALSE;
492 : : }
493 : 0 : tsd->num_occur_rem = remOccur;
494 : 0 : return TRUE;
495 : : }
496 : :
497 : : static
498 : : gboolean
499 : 0 : sx_defer_inst_count_handler (xmlNodePtr node, gpointer gpTSD)
500 : : {
501 : : gint64 instCount;
502 : 0 : SXTmpStateData* tsd = (SXTmpStateData*)gpTSD;
503 : 0 : g_return_val_if_fail (node, FALSE);
504 : :
505 : 0 : if (! dom_tree_to_integer (node, &instCount))
506 : : {
507 : 0 : return FALSE;
508 : : }
509 : 0 : tsd->num_inst = instCount;
510 : 0 : return TRUE;
511 : : }
512 : :
513 : : struct dom_tree_handler sx_defer_dom_handlers[] =
514 : : {
515 : : /* tag name, handler, opt, ? */
516 : : { SX_LAST, sx_defer_last_handler, 1, 0 },
517 : : { SX_REM_OCCUR, sx_defer_rem_occur_handler, 1, 0 },
518 : : { SX_INSTANCE_COUNT, sx_defer_inst_count_handler, 1, 0 },
519 : : { NULL, NULL, 0, 0 }
520 : : };
521 : :
522 : : static
523 : : gboolean
524 : 0 : sx_defer_inst_handler (xmlNodePtr node, gpointer sx_pdata)
525 : : {
526 : 0 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
527 : 0 : SchedXaction* sx = pdata->sx;
528 : : SXTmpStateData* tsd;
529 : :
530 : 0 : g_return_val_if_fail (node, FALSE);
531 : :
532 : 0 : tsd = g_new0 (SXTmpStateData, 1);
533 : : g_assert (sx_defer_dom_handlers != NULL);
534 : 0 : if (!dom_tree_generic_parse (node,
535 : : sx_defer_dom_handlers,
536 : : tsd))
537 : : {
538 : 0 : xmlElemDump (stdout, NULL, node);
539 : 0 : g_free (tsd);
540 : 0 : tsd = NULL;
541 : 0 : return FALSE;
542 : : }
543 : :
544 : : /* We assume they were serialized in sorted order, here. */
545 : 0 : sx->deferredList = g_list_append (sx->deferredList, tsd);
546 : 0 : return TRUE;
547 : : }
548 : :
549 : : static
550 : : gboolean
551 : 1 : sx_numOccur_handler (xmlNodePtr node, gpointer sx_pdata)
552 : : {
553 : 1 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
554 : 1 : SchedXaction* sx = pdata->sx;
555 : : gint64 numOccur;
556 : :
557 : 1 : if (! dom_tree_to_integer (node, &numOccur))
558 : : {
559 : 0 : return FALSE;
560 : : }
561 : :
562 : 1 : xaccSchedXactionSetNumOccur (sx, numOccur);
563 : :
564 : 1 : return TRUE;
565 : : }
566 : :
567 : :
568 : : static
569 : : gboolean
570 : 5 : sx_templ_acct_handler (xmlNodePtr node, gpointer sx_pdata)
571 : : {
572 : 5 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
573 : 5 : SchedXaction* sx = pdata->sx;
574 : 5 : auto templ_acct_guid = dom_tree_to_guid (node);
575 : : Account* account;
576 : :
577 : 5 : if (!templ_acct_guid)
578 : : {
579 : 0 : return FALSE;
580 : : }
581 : :
582 : 5 : account = xaccAccountLookup (&*templ_acct_guid, pdata->book);
583 : 5 : sx_set_template_account (sx, account);
584 : :
585 : 5 : return TRUE;
586 : : }
587 : :
588 : :
589 : : static
590 : : gboolean
591 : 1 : sx_remOccur_handler (xmlNodePtr node, gpointer sx_pdata)
592 : : {
593 : 1 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
594 : 1 : SchedXaction* sx = pdata->sx;
595 : : gint64 remOccur;
596 : :
597 : 1 : if (! dom_tree_to_integer (node, &remOccur))
598 : : {
599 : 0 : return FALSE;
600 : : }
601 : :
602 : 1 : xaccSchedXactionSetRemOccur (sx, remOccur);
603 : :
604 : 1 : return TRUE;
605 : : }
606 : :
607 : : static
608 : : gboolean
609 : 0 : sx_slots_handler (xmlNodePtr node, gpointer sx_pdata)
610 : : {
611 : 0 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
612 : 0 : SchedXaction* sx = pdata->sx;
613 : :
614 : 0 : return dom_tree_create_instance_slots (node, QOF_INSTANCE (sx));
615 : : }
616 : :
617 : : struct dom_tree_handler sx_dom_handlers[] =
618 : : {
619 : : { SX_ID, sx_id_handler, 1, 0 },
620 : : { SX_NAME, sx_name_handler, 1, 0 },
621 : : { SX_ENABLED, sx_enabled_handler, 0, 0 },
622 : : { SX_AUTOCREATE, sx_autoCreate_handler, 1, 0 },
623 : : { SX_AUTOCREATE_NOTIFY, sx_notify_handler, 1, 0 },
624 : : { SX_ADVANCE_CREATE_DAYS, sx_advCreate_handler, 1, 0 },
625 : : { SX_ADVANCE_REMIND_DAYS, sx_advRemind_handler, 1, 0 },
626 : : { SX_INSTANCE_COUNT, sx_instcount_handler, 0, 0 },
627 : : { SX_START, sx_start_handler, 1, 0 },
628 : : { SX_LAST, sx_last_handler, 0, 0 },
629 : : { SX_NUM_OCCUR, sx_numOccur_handler, 0, 0 },
630 : : { SX_REM_OCCUR, sx_remOccur_handler, 0, 0 },
631 : : { SX_END, sx_end_handler, 0, 0 },
632 : : { SX_TEMPL_ACCT, sx_templ_acct_handler, 0, 0 },
633 : : { SX_FREQSPEC, sx_freqspec_handler, 0, 0 },
634 : : { SX_SCHEDULE, sx_recurrence_handler, 0, 0 },
635 : : { SX_DEFER_INSTANCE, sx_defer_inst_handler, 0, 0 },
636 : : { SX_SLOTS, sx_slots_handler, 0, 0 },
637 : : { NULL, NULL, 0, 0 }
638 : : };
639 : :
640 : : static gboolean
641 : 100 : gnc_schedXaction_end_handler (gpointer data_for_children,
642 : : GSList* data_from_children, GSList* sibling_data,
643 : : gpointer parent_data, gpointer global_data,
644 : : gpointer* result, const gchar* tag)
645 : : {
646 : : SchedXaction* sx;
647 : 100 : gboolean successful = FALSE;
648 : 100 : xmlNodePtr tree = (xmlNodePtr)data_for_children;
649 : 100 : gxpf_data* gdata = (gxpf_data*)global_data;
650 : : struct sx_pdata sx_pdata;
651 : :
652 : 100 : if (parent_data)
653 : : {
654 : 95 : return TRUE;
655 : : }
656 : :
657 : 5 : if (!tag)
658 : : {
659 : 0 : return TRUE;
660 : : }
661 : :
662 : 5 : g_return_val_if_fail (tree, FALSE);
663 : :
664 : 5 : sx = xaccSchedXactionMalloc (static_cast<QofBook*> (gdata->bookdata));
665 : :
666 : 5 : memset (&sx_pdata, 0, sizeof (sx_pdata));
667 : 5 : sx_pdata.sx = sx;
668 : 5 : sx_pdata.book = static_cast<decltype (sx_pdata.book)> (gdata->bookdata);
669 : :
670 : : g_assert (sx_dom_handlers != NULL);
671 : :
672 : 5 : successful = dom_tree_generic_parse (tree, sx_dom_handlers, &sx_pdata);
673 : 5 : if (!successful)
674 : : {
675 : 0 : g_critical ("failed to parse scheduled xaction");
676 : 0 : xmlElemDump (stdout, NULL, tree);
677 : 0 : gnc_sx_begin_edit (sx);
678 : 0 : xaccSchedXactionDestroy (sx);
679 : 0 : goto done;
680 : : }
681 : :
682 : 5 : if (tree->properties)
683 : : {
684 : 5 : gchar* sx_name = xaccSchedXactionGetName (sx);
685 : : xmlAttr* attr;
686 : 10 : for (attr = tree->properties; attr != NULL; attr = attr->next)
687 : : {
688 : 5 : xmlChar* attr_value = attr->children->content;
689 : 5 : DEBUG ("sx attribute name[%s] value[%s]", attr->name, attr_value);
690 : 5 : if (strcmp ((const char*)attr->name, "version") != 0)
691 : : {
692 : 0 : g_warning ("unknown sx attribute [%s]", attr->name);
693 : 0 : continue;
694 : : }
695 : :
696 : : // if version == 1.0.0: ensure freqspec, no recurrence
697 : : // if version == 2.0.0: ensure recurrence, no freqspec.
698 : 5 : if (strcmp ((const char*)attr_value,
699 : : schedxaction_version_string) == 0)
700 : : {
701 : 0 : if (!sx_pdata.saw_freqspec)
702 : 0 : g_critical ("did not see freqspec in version 1 sx [%s]", sx_name);
703 : 0 : if (sx_pdata.saw_recurrence)
704 : 0 : g_warning ("saw recurrence in supposedly version 1 sx [%s]", sx_name);
705 : : }
706 : :
707 : 5 : if (strcmp ((const char*)attr_value,
708 : : schedxaction_version2_string) == 0)
709 : : {
710 : 5 : if (sx_pdata.saw_freqspec)
711 : 0 : g_warning ("saw freqspec in version 2 sx [%s]", sx_name);
712 : 5 : if (!sx_pdata.saw_recurrence)
713 : 0 : g_critical ("did not find recurrence in version 2 sx [%s]", sx_name);
714 : : }
715 : : }
716 : : }
717 : :
718 : : // generic_callback -> book_callback: insert the SX in the book
719 : 5 : gdata->cb (tag, gdata->parsedata, sx);
720 : :
721 : : /* FIXME: this should be removed somewhere near 1.8 release time. */
722 : 5 : if (sx->template_acct == NULL)
723 : : {
724 : 0 : Account* ra = NULL;
725 : 0 : Account* acct = NULL;
726 : 0 : sixtp_gdv2* sixdata = static_cast<decltype (sixdata)> (gdata->parsedata);
727 : : QofBook* book;
728 : : gchar guidstr[GUID_ENCODING_LENGTH + 1];
729 : :
730 : 0 : book = sixdata->book;
731 : :
732 : : /* We're dealing with a pre-200107<near-end-of-month> rgmerk
733 : : change re: storing template accounts. */
734 : : /* Fix: get account with name of our GncGUID from the template
735 : : accounts. Make that our template_acct pointer. */
736 : 0 : guid_to_string_buff (xaccSchedXactionGetGUID (sx), guidstr);
737 : 0 : ra = gnc_book_get_template_root (book);
738 : 0 : if (ra == NULL)
739 : : {
740 : 0 : g_warning ("Error getting template root account from being-parsed Book.");
741 : 0 : xmlFreeNode (tree);
742 : 0 : return FALSE;
743 : : }
744 : 0 : acct = gnc_account_lookup_by_name (ra, guidstr);
745 : 0 : if (acct == NULL)
746 : : {
747 : 0 : g_warning ("no template account with name [%s]", guidstr);
748 : 0 : xmlFreeNode (tree);
749 : 0 : return FALSE;
750 : : }
751 : 0 : DEBUG ("template account name [%s] for SX with GncGUID [%s]",
752 : : xaccAccountGetName (acct), guidstr);
753 : :
754 : : /* FIXME: free existing template account.
755 : : * HUH????? We only execute this if there isn't
756 : : * currently an existing template account, don't we?
757 : : * <rgmerk>
758 : : */
759 : :
760 : 0 : sx->template_acct = acct;
761 : : }
762 : :
763 : 5 : done:
764 : 5 : xmlFreeNode (tree);
765 : :
766 : 5 : return successful;
767 : : }
768 : :
769 : : sixtp*
770 : 44 : gnc_schedXaction_sixtp_parser_create (void)
771 : : {
772 : 44 : return sixtp_dom_parser_new (gnc_schedXaction_end_handler, NULL, NULL);
773 : : }
774 : :
775 : : static
776 : : gboolean
777 : 8 : tt_act_handler (xmlNodePtr node, gpointer data)
778 : : {
779 : 8 : gnc_template_xaction_data* txd = static_cast<decltype (txd)> (data);
780 : : Account* acc;
781 : : gnc_commodity* com;
782 : :
783 : 8 : acc = dom_tree_to_account (node, txd->book);
784 : :
785 : 8 : if (acc == NULL)
786 : : {
787 : 0 : return FALSE;
788 : : }
789 : : else
790 : : {
791 : 8 : xaccAccountBeginEdit (acc);
792 : :
793 : : /* Check for the lack of a commodity [signifying that the
794 : : pre-7/11/2001-CIT-change SX template Account was parsed [but
795 : : incorrectly]. */
796 : 8 : if (xaccAccountGetCommodity (acc) == NULL)
797 : : {
798 : : #if 1
799 : : gnc_commodity_table* table;
800 : :
801 : 1 : table = gnc_commodity_table_get_table (txd->book);
802 : 1 : com = gnc_commodity_table_lookup (table,
803 : : GNC_COMMODITY_NS_TEMPLATE, "template");
804 : : #else
805 : : /* FIXME: This should first look in the table of the
806 : : book, maybe? The right thing happens [WRT file
807 : : load/save] if we just _new all the time, but it
808 : : doesn't seem right. This whole block should go
809 : : away at some point, but the same concern still
810 : : applies for
811 : : SchedXaction.c:xaccSchedXactionInit... */
812 : : com = gnc_commodity_new (txd->book,
813 : : "template", GNC_COMMODITY_NS_TEMPLATE,
814 : : "template", "template",
815 : : 1);
816 : : #endif
817 : 1 : xaccAccountSetCommodity (acc, com);
818 : : }
819 : :
820 : 8 : txd->accts = g_list_append (txd->accts, acc);
821 : : }
822 : :
823 : 8 : return TRUE;
824 : : }
825 : :
826 : : static
827 : : gboolean
828 : 5 : tt_trn_handler (xmlNodePtr node, gpointer data)
829 : : {
830 : 5 : gnc_template_xaction_data* txd = static_cast<decltype (txd)> (data);
831 : : Transaction* trn;
832 : :
833 : 5 : trn = dom_tree_to_transaction (node, txd->book);
834 : :
835 : 5 : if (trn == NULL)
836 : : {
837 : 0 : return FALSE;
838 : : }
839 : : else
840 : : {
841 : 5 : txd->transactions = g_list_append (txd->transactions, trn);
842 : : }
843 : :
844 : 5 : return TRUE;
845 : : }
846 : :
847 : : struct dom_tree_handler tt_dom_handlers[] =
848 : : {
849 : : { GNC_ACCOUNT_TAG, tt_act_handler, 0, 0 },
850 : : { GNC_TRANSACTION_TAG, tt_trn_handler, 0, 0 },
851 : : { NULL, NULL, 0, 0 },
852 : : };
853 : :
854 : : static gboolean
855 : 373 : gnc_template_transaction_end_handler (gpointer data_for_children,
856 : : GSList* data_from_children,
857 : : GSList* sibling_data,
858 : : gpointer parent_data,
859 : : gpointer global_data,
860 : : gpointer* result,
861 : : const gchar* tag)
862 : : {
863 : 373 : gboolean successful = FALSE;
864 : 373 : xmlNodePtr tree = static_cast<decltype (tree)> (data_for_children);
865 : 373 : gxpf_data* gdata = static_cast<decltype (gdata)> (global_data);
866 : 373 : QofBook* book = static_cast<decltype (book)> (gdata->bookdata);
867 : : GList* n;
868 : : gnc_template_xaction_data txd;
869 : :
870 : 373 : txd.book = book;
871 : 373 : txd.accts = NULL;
872 : 373 : txd.transactions = NULL;
873 : :
874 : : /* the DOM tree will have an account tree [the template
875 : : accounts] and a list of transactions [which will be members
876 : : of the template account].
877 : :
878 : : we want to parse through the dom trees for each, placing
879 : : the null-parent account in the book's template-group slot,
880 : : the others under it, and the transactions as normal. */
881 : :
882 : 373 : if (parent_data)
883 : : {
884 : 370 : return TRUE;
885 : : }
886 : :
887 : 3 : if (!tag)
888 : : {
889 : 0 : return TRUE;
890 : : }
891 : :
892 : 3 : g_return_val_if_fail (tree, FALSE);
893 : :
894 : 3 : successful = dom_tree_generic_parse (tree, tt_dom_handlers, &txd);
895 : :
896 : 3 : if (successful)
897 : : {
898 : 3 : gdata->cb (tag, gdata->parsedata, &txd);
899 : : }
900 : : else
901 : : {
902 : 0 : g_warning ("failed to parse template transaction");
903 : 0 : xmlElemDump (stdout, NULL, tree);
904 : : }
905 : :
906 : : /* cleanup */
907 : 11 : for (n = txd.accts; n; n = n->next)
908 : : {
909 : 8 : n->data = NULL;
910 : : }
911 : 8 : for (n = txd.transactions; n; n = n->next)
912 : : {
913 : 5 : n->data = NULL;
914 : : }
915 : 3 : g_list_free (txd.accts);
916 : 3 : g_list_free (txd.transactions);
917 : :
918 : 3 : xmlFreeNode (tree);
919 : :
920 : 3 : return successful;
921 : : }
922 : :
923 : : sixtp*
924 : 44 : gnc_template_transaction_sixtp_parser_create (void)
925 : : {
926 : 44 : return sixtp_dom_parser_new (gnc_template_transaction_end_handler,
927 : 44 : NULL, NULL);
928 : : }
|