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 : GncGUID* tmp = dom_tree_to_guid (node);
214 : :
215 : 5 : g_return_val_if_fail (tmp, FALSE);
216 : 5 : xaccSchedXactionSetGUID (sx, tmp);
217 : 5 : guid_free (tmp);
218 : :
219 : 5 : return TRUE;
220 : : }
221 : :
222 : : static
223 : : gboolean
224 : 5 : sx_name_handler (xmlNodePtr node, gpointer sx_pdata)
225 : : {
226 : 5 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
227 : 5 : SchedXaction* sx = pdata->sx;
228 : 5 : gchar* tmp = dom_tree_to_text (node);
229 : 5 : DEBUG ("sx named [%s]", tmp);
230 : 5 : g_return_val_if_fail (tmp, FALSE);
231 : 5 : xaccSchedXactionSetName (sx, tmp);
232 : 5 : g_free (tmp);
233 : 5 : return TRUE;
234 : : }
235 : :
236 : : static gboolean
237 : 5 : sx_enabled_handler (xmlNodePtr node, gpointer sx_pdata)
238 : : {
239 : 5 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
240 : 5 : SchedXaction* sx = pdata->sx;
241 : 5 : gchar* tmp = dom_tree_to_text (node);
242 : :
243 : 5 : sx->enabled = (g_strcmp0 (tmp, "y") == 0 ? TRUE : FALSE);
244 : 5 : g_free (tmp);
245 : :
246 : 5 : return TRUE;
247 : : }
248 : :
249 : : static gboolean
250 : 5 : sx_autoCreate_handler (xmlNodePtr node, gpointer sx_pdata)
251 : : {
252 : 5 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
253 : 5 : SchedXaction* sx = pdata->sx;
254 : 5 : gchar* tmp = dom_tree_to_text (node);
255 : :
256 : 5 : sx->autoCreateOption = (g_strcmp0 (tmp, "y") == 0 ? TRUE : FALSE);
257 : 5 : g_free (tmp);
258 : :
259 : 5 : return TRUE;
260 : : }
261 : :
262 : : static gboolean
263 : 5 : sx_notify_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 : 5 : gchar* tmp = dom_tree_to_text (node);
268 : :
269 : 5 : sx->autoCreateNotify = (g_strcmp0 (tmp, "y") == 0 ? TRUE : FALSE);
270 : 5 : g_free (tmp);
271 : :
272 : 5 : return TRUE;
273 : : }
274 : :
275 : : static gboolean
276 : 5 : sx_advCreate_handler (xmlNodePtr node, gpointer sx_pdata)
277 : : {
278 : 5 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
279 : 5 : SchedXaction* sx = pdata->sx;
280 : : gint64 advCreate;
281 : :
282 : 5 : if (! dom_tree_to_integer (node, &advCreate))
283 : : {
284 : 0 : return FALSE;
285 : : }
286 : :
287 : 5 : xaccSchedXactionSetAdvanceCreation (sx, advCreate);
288 : 5 : return TRUE;
289 : : }
290 : :
291 : : static gboolean
292 : 5 : sx_advRemind_handler (xmlNodePtr node, gpointer sx_pdata)
293 : : {
294 : 5 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
295 : 5 : SchedXaction* sx = pdata->sx;
296 : : gint64 advRemind;
297 : :
298 : 5 : if (! dom_tree_to_integer (node, &advRemind))
299 : : {
300 : 0 : return FALSE;
301 : : }
302 : :
303 : 5 : xaccSchedXactionSetAdvanceReminder (sx, advRemind);
304 : 5 : return TRUE;
305 : : }
306 : :
307 : : static
308 : : gboolean
309 : 8 : sx_set_date (xmlNodePtr node, SchedXaction* sx,
310 : : void (*settor) (SchedXaction* sx, const GDate* d))
311 : : {
312 : : GDate* date;
313 : 8 : date = dom_tree_to_gdate (node);
314 : 8 : g_return_val_if_fail (date, FALSE);
315 : 8 : (*settor) (sx, date);
316 : 8 : g_date_free (date);
317 : :
318 : 8 : return TRUE;
319 : : }
320 : :
321 : : static
322 : : gboolean
323 : 5 : sx_instcount_handler (xmlNodePtr node, gpointer sx_pdata)
324 : : {
325 : 5 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
326 : 5 : SchedXaction* sx = pdata->sx;
327 : : gint64 instanceNum;
328 : :
329 : 5 : if (! dom_tree_to_integer (node, &instanceNum))
330 : : {
331 : 0 : return FALSE;
332 : : }
333 : :
334 : 5 : gnc_sx_set_instance_count (sx, instanceNum);
335 : 5 : return TRUE;
336 : : }
337 : :
338 : : static
339 : : gboolean
340 : 5 : sx_start_handler (xmlNodePtr node, gpointer sx_pdata)
341 : : {
342 : 5 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
343 : 5 : SchedXaction* sx = pdata->sx;
344 : :
345 : 5 : return sx_set_date (node, sx, xaccSchedXactionSetStartDate);
346 : : }
347 : :
348 : : static
349 : : gboolean
350 : 2 : sx_last_handler (xmlNodePtr node, gpointer sx_pdata)
351 : : {
352 : 2 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
353 : 2 : SchedXaction* sx = pdata->sx;
354 : :
355 : 2 : return sx_set_date (node, sx, xaccSchedXactionSetLastOccurDate);
356 : : }
357 : :
358 : : static
359 : : gboolean
360 : 1 : sx_end_handler (xmlNodePtr node, gpointer sx_pdata)
361 : : {
362 : 1 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
363 : 1 : SchedXaction* sx = pdata->sx;
364 : :
365 : 1 : return sx_set_date (node, sx, xaccSchedXactionSetEndDate);
366 : : }
367 : :
368 : : static void
369 : 0 : _fixup_recurrence_start_dates (const GDate* sx_start_date, GList* schedule)
370 : : {
371 : : GList* iter;
372 : 0 : for (iter = schedule; iter != NULL; iter = iter->next)
373 : : {
374 : : Recurrence* r;
375 : : GDate start, next;
376 : :
377 : 0 : r = (Recurrence*)iter->data;
378 : :
379 : 0 : start = *sx_start_date;
380 : 0 : g_date_subtract_days (&start, 1);
381 : :
382 : 0 : g_date_clear (&next, 1);
383 : :
384 : 0 : recurrenceNextInstance (r, &start, &next);
385 : 0 : g_return_if_fail (g_date_valid (&next));
386 : :
387 : : {
388 : : gchar date_str[128];
389 : : gchar* sched_str;
390 : :
391 : 0 : g_date_strftime (date_str, 127, "%x", &next);
392 : 0 : sched_str = recurrenceToString (r);
393 : 0 : DEBUG ("setting recurrence [%s] start date to [%s]",
394 : : sched_str, date_str);
395 : 0 : g_free (sched_str);
396 : : }
397 : :
398 : 0 : recurrenceSet (r,
399 : 0 : recurrenceGetMultiplier (r),
400 : : recurrenceGetPeriodType (r),
401 : : &next,
402 : : recurrenceGetWeekendAdjust (r));
403 : : }
404 : :
405 : 0 : if (g_list_length (schedule) == 1
406 : 0 : && recurrenceGetPeriodType ((Recurrence*)g_list_nth_data (schedule,
407 : : 0)) == PERIOD_ONCE)
408 : : {
409 : : char date_buf[128];
410 : 0 : Recurrence* fixup = (Recurrence*)g_list_nth_data (schedule, 0);
411 : 0 : g_date_strftime (date_buf, 127, "%x", sx_start_date);
412 : 0 : recurrenceSet (fixup, 1, PERIOD_ONCE, sx_start_date, WEEKEND_ADJ_NONE);
413 : 0 : DEBUG ("fixed up period=ONCE Recurrence to date [%s]", date_buf);
414 : : }
415 : : }
416 : :
417 : : static gboolean
418 : 0 : sx_freqspec_handler (xmlNodePtr node, gpointer sx_pdata)
419 : : {
420 : 0 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
421 : 0 : SchedXaction* sx = pdata->sx;
422 : : GList* schedule;
423 : : gchar* debug_str;
424 : :
425 : 0 : g_return_val_if_fail (node, FALSE);
426 : :
427 : 0 : schedule = dom_tree_freqSpec_to_recurrences (node, pdata->book);
428 : 0 : gnc_sx_set_schedule (sx, schedule);
429 : 0 : debug_str = recurrenceListToString (schedule);
430 : 0 : DEBUG ("parsed from freqspec [%s]", debug_str);
431 : 0 : g_free (debug_str);
432 : :
433 : 0 : _fixup_recurrence_start_dates (xaccSchedXactionGetStartDate (sx), schedule);
434 : 0 : pdata->saw_freqspec = TRUE;
435 : :
436 : 0 : return TRUE;
437 : : }
438 : :
439 : : static gboolean
440 : 5 : sx_schedule_recurrence_handler (xmlNodePtr node, gpointer parsing_data)
441 : : {
442 : 5 : GList** schedule = (GList**)parsing_data;
443 : : gchar* sched_str;
444 : 5 : Recurrence* r = dom_tree_to_recurrence (node);
445 : 5 : g_return_val_if_fail (r, FALSE);
446 : 5 : sched_str = recurrenceToString (r);
447 : 5 : DEBUG ("parsed recurrence [%s]", sched_str);
448 : 5 : g_free (sched_str);
449 : 5 : *schedule = g_list_append (*schedule, r);
450 : 5 : return TRUE;
451 : : }
452 : :
453 : : struct dom_tree_handler sx_recurrence_list_handlers[] =
454 : : {
455 : : { "gnc:recurrence", sx_schedule_recurrence_handler, 0, 0 },
456 : : { NULL, NULL, 0, 0 }
457 : : };
458 : :
459 : : static gboolean
460 : 5 : sx_recurrence_handler (xmlNodePtr node, gpointer _pdata)
461 : : {
462 : 5 : struct sx_pdata* parsing_data = static_cast<decltype (parsing_data)> (_pdata);
463 : 5 : GList* schedule = NULL;
464 : : gchar* debug_str;
465 : :
466 : 5 : g_return_val_if_fail (node, FALSE);
467 : :
468 : 5 : if (!dom_tree_generic_parse (node, sx_recurrence_list_handlers, &schedule))
469 : 0 : return FALSE;
470 : : // g_return_val_if_fail(schedule, FALSE);
471 : 5 : debug_str = recurrenceListToString (schedule);
472 : 5 : DEBUG ("setting freshly-parsed schedule: [%s]", debug_str);
473 : 5 : g_free (debug_str);
474 : 5 : gnc_sx_set_schedule (parsing_data->sx, schedule);
475 : 5 : parsing_data->saw_recurrence = TRUE;
476 : 5 : return TRUE;
477 : : }
478 : :
479 : : static
480 : : gboolean
481 : 0 : sx_defer_last_handler (xmlNodePtr node, gpointer gpTSD)
482 : : {
483 : : GDate* gd;
484 : 0 : SXTmpStateData* tsd = (SXTmpStateData*)gpTSD;
485 : :
486 : 0 : g_return_val_if_fail (node, FALSE);
487 : 0 : gd = dom_tree_to_gdate (node);
488 : 0 : g_return_val_if_fail (gd, FALSE);
489 : 0 : tsd->last_date = *gd;
490 : 0 : g_date_free (gd);
491 : 0 : return TRUE;
492 : : }
493 : :
494 : : static
495 : : gboolean
496 : 0 : sx_defer_rem_occur_handler (xmlNodePtr node, gpointer gpTSD)
497 : : {
498 : : gint64 remOccur;
499 : 0 : SXTmpStateData* tsd = (SXTmpStateData*)gpTSD;
500 : 0 : g_return_val_if_fail (node, FALSE);
501 : :
502 : 0 : if (! dom_tree_to_integer (node, &remOccur))
503 : : {
504 : 0 : return FALSE;
505 : : }
506 : 0 : tsd->num_occur_rem = remOccur;
507 : 0 : return TRUE;
508 : : }
509 : :
510 : : static
511 : : gboolean
512 : 0 : sx_defer_inst_count_handler (xmlNodePtr node, gpointer gpTSD)
513 : : {
514 : : gint64 instCount;
515 : 0 : SXTmpStateData* tsd = (SXTmpStateData*)gpTSD;
516 : 0 : g_return_val_if_fail (node, FALSE);
517 : :
518 : 0 : if (! dom_tree_to_integer (node, &instCount))
519 : : {
520 : 0 : return FALSE;
521 : : }
522 : 0 : tsd->num_inst = instCount;
523 : 0 : return TRUE;
524 : : }
525 : :
526 : : struct dom_tree_handler sx_defer_dom_handlers[] =
527 : : {
528 : : /* tag name, handler, opt, ? */
529 : : { SX_LAST, sx_defer_last_handler, 1, 0 },
530 : : { SX_REM_OCCUR, sx_defer_rem_occur_handler, 1, 0 },
531 : : { SX_INSTANCE_COUNT, sx_defer_inst_count_handler, 1, 0 },
532 : : { NULL, NULL, 0, 0 }
533 : : };
534 : :
535 : : static
536 : : gboolean
537 : 0 : sx_defer_inst_handler (xmlNodePtr node, gpointer sx_pdata)
538 : : {
539 : 0 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
540 : 0 : SchedXaction* sx = pdata->sx;
541 : : SXTmpStateData* tsd;
542 : :
543 : 0 : g_return_val_if_fail (node, FALSE);
544 : :
545 : 0 : tsd = g_new0 (SXTmpStateData, 1);
546 : : g_assert (sx_defer_dom_handlers != NULL);
547 : 0 : if (!dom_tree_generic_parse (node,
548 : : sx_defer_dom_handlers,
549 : : tsd))
550 : : {
551 : 0 : xmlElemDump (stdout, NULL, node);
552 : 0 : g_free (tsd);
553 : 0 : tsd = NULL;
554 : 0 : return FALSE;
555 : : }
556 : :
557 : : /* We assume they were serialized in sorted order, here. */
558 : 0 : sx->deferredList = g_list_append (sx->deferredList, tsd);
559 : 0 : return TRUE;
560 : : }
561 : :
562 : : static
563 : : gboolean
564 : 1 : sx_numOccur_handler (xmlNodePtr node, gpointer sx_pdata)
565 : : {
566 : 1 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
567 : 1 : SchedXaction* sx = pdata->sx;
568 : : gint64 numOccur;
569 : :
570 : 1 : if (! dom_tree_to_integer (node, &numOccur))
571 : : {
572 : 0 : return FALSE;
573 : : }
574 : :
575 : 1 : xaccSchedXactionSetNumOccur (sx, numOccur);
576 : :
577 : 1 : return TRUE;
578 : : }
579 : :
580 : :
581 : : static
582 : : gboolean
583 : 5 : sx_templ_acct_handler (xmlNodePtr node, gpointer sx_pdata)
584 : : {
585 : 5 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
586 : 5 : SchedXaction* sx = pdata->sx;
587 : 5 : GncGUID* templ_acct_guid = dom_tree_to_guid (node);
588 : : Account* account;
589 : :
590 : 5 : if (!templ_acct_guid)
591 : : {
592 : 0 : return FALSE;
593 : : }
594 : :
595 : 5 : account = xaccAccountLookup (templ_acct_guid, pdata->book);
596 : 5 : sx_set_template_account (sx, account);
597 : 5 : guid_free (templ_acct_guid);
598 : :
599 : 5 : return TRUE;
600 : : }
601 : :
602 : :
603 : : static
604 : : gboolean
605 : 1 : sx_remOccur_handler (xmlNodePtr node, gpointer sx_pdata)
606 : : {
607 : 1 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
608 : 1 : SchedXaction* sx = pdata->sx;
609 : : gint64 remOccur;
610 : :
611 : 1 : if (! dom_tree_to_integer (node, &remOccur))
612 : : {
613 : 0 : return FALSE;
614 : : }
615 : :
616 : 1 : xaccSchedXactionSetRemOccur (sx, remOccur);
617 : :
618 : 1 : return TRUE;
619 : : }
620 : :
621 : : static
622 : : gboolean
623 : 0 : sx_slots_handler (xmlNodePtr node, gpointer sx_pdata)
624 : : {
625 : 0 : struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
626 : 0 : SchedXaction* sx = pdata->sx;
627 : :
628 : 0 : return dom_tree_create_instance_slots (node, QOF_INSTANCE (sx));
629 : : }
630 : :
631 : : struct dom_tree_handler sx_dom_handlers[] =
632 : : {
633 : : { SX_ID, sx_id_handler, 1, 0 },
634 : : { SX_NAME, sx_name_handler, 1, 0 },
635 : : { SX_ENABLED, sx_enabled_handler, 0, 0 },
636 : : { SX_AUTOCREATE, sx_autoCreate_handler, 1, 0 },
637 : : { SX_AUTOCREATE_NOTIFY, sx_notify_handler, 1, 0 },
638 : : { SX_ADVANCE_CREATE_DAYS, sx_advCreate_handler, 1, 0 },
639 : : { SX_ADVANCE_REMIND_DAYS, sx_advRemind_handler, 1, 0 },
640 : : { SX_INSTANCE_COUNT, sx_instcount_handler, 0, 0 },
641 : : { SX_START, sx_start_handler, 1, 0 },
642 : : { SX_LAST, sx_last_handler, 0, 0 },
643 : : { SX_NUM_OCCUR, sx_numOccur_handler, 0, 0 },
644 : : { SX_REM_OCCUR, sx_remOccur_handler, 0, 0 },
645 : : { SX_END, sx_end_handler, 0, 0 },
646 : : { SX_TEMPL_ACCT, sx_templ_acct_handler, 0, 0 },
647 : : { SX_FREQSPEC, sx_freqspec_handler, 0, 0 },
648 : : { SX_SCHEDULE, sx_recurrence_handler, 0, 0 },
649 : : { SX_DEFER_INSTANCE, sx_defer_inst_handler, 0, 0 },
650 : : { SX_SLOTS, sx_slots_handler, 0, 0 },
651 : : { NULL, NULL, 0, 0 }
652 : : };
653 : :
654 : : static gboolean
655 : 100 : gnc_schedXaction_end_handler (gpointer data_for_children,
656 : : GSList* data_from_children, GSList* sibling_data,
657 : : gpointer parent_data, gpointer global_data,
658 : : gpointer* result, const gchar* tag)
659 : : {
660 : : SchedXaction* sx;
661 : 100 : gboolean successful = FALSE;
662 : 100 : xmlNodePtr tree = (xmlNodePtr)data_for_children;
663 : 100 : gxpf_data* gdata = (gxpf_data*)global_data;
664 : : struct sx_pdata sx_pdata;
665 : :
666 : 100 : if (parent_data)
667 : : {
668 : 95 : return TRUE;
669 : : }
670 : :
671 : 5 : if (!tag)
672 : : {
673 : 0 : return TRUE;
674 : : }
675 : :
676 : 5 : g_return_val_if_fail (tree, FALSE);
677 : :
678 : 5 : sx = xaccSchedXactionMalloc (static_cast<QofBook*> (gdata->bookdata));
679 : :
680 : 5 : memset (&sx_pdata, 0, sizeof (sx_pdata));
681 : 5 : sx_pdata.sx = sx;
682 : 5 : sx_pdata.book = static_cast<decltype (sx_pdata.book)> (gdata->bookdata);
683 : :
684 : : g_assert (sx_dom_handlers != NULL);
685 : :
686 : 5 : successful = dom_tree_generic_parse (tree, sx_dom_handlers, &sx_pdata);
687 : 5 : if (!successful)
688 : : {
689 : 0 : g_critical ("failed to parse scheduled xaction");
690 : 0 : xmlElemDump (stdout, NULL, tree);
691 : 0 : gnc_sx_begin_edit (sx);
692 : 0 : xaccSchedXactionDestroy (sx);
693 : 0 : goto done;
694 : : }
695 : :
696 : 5 : if (tree->properties)
697 : : {
698 : 5 : gchar* sx_name = xaccSchedXactionGetName (sx);
699 : : xmlAttr* attr;
700 : 10 : for (attr = tree->properties; attr != NULL; attr = attr->next)
701 : : {
702 : 5 : xmlChar* attr_value = attr->children->content;
703 : 5 : DEBUG ("sx attribute name[%s] value[%s]", attr->name, attr_value);
704 : 5 : if (strcmp ((const char*)attr->name, "version") != 0)
705 : : {
706 : 0 : g_warning ("unknown sx attribute [%s]", attr->name);
707 : 0 : continue;
708 : : }
709 : :
710 : : // if version == 1.0.0: ensure freqspec, no recurrence
711 : : // if version == 2.0.0: ensure recurrence, no freqspec.
712 : 5 : if (strcmp ((const char*)attr_value,
713 : : schedxaction_version_string) == 0)
714 : : {
715 : 0 : if (!sx_pdata.saw_freqspec)
716 : 0 : g_critical ("did not see freqspec in version 1 sx [%s]", sx_name);
717 : 0 : if (sx_pdata.saw_recurrence)
718 : 0 : g_warning ("saw recurrence in supposedly version 1 sx [%s]", sx_name);
719 : : }
720 : :
721 : 5 : if (strcmp ((const char*)attr_value,
722 : : schedxaction_version2_string) == 0)
723 : : {
724 : 5 : if (sx_pdata.saw_freqspec)
725 : 0 : g_warning ("saw freqspec in version 2 sx [%s]", sx_name);
726 : 5 : if (!sx_pdata.saw_recurrence)
727 : 0 : g_critical ("did not find recurrence in version 2 sx [%s]", sx_name);
728 : : }
729 : : }
730 : : }
731 : :
732 : : // generic_callback -> book_callback: insert the SX in the book
733 : 5 : gdata->cb (tag, gdata->parsedata, sx);
734 : :
735 : : /* FIXME: this should be removed somewhere near 1.8 release time. */
736 : 5 : if (sx->template_acct == NULL)
737 : : {
738 : 0 : Account* ra = NULL;
739 : 0 : Account* acct = NULL;
740 : 0 : sixtp_gdv2* sixdata = static_cast<decltype (sixdata)> (gdata->parsedata);
741 : : QofBook* book;
742 : : gchar guidstr[GUID_ENCODING_LENGTH + 1];
743 : :
744 : 0 : book = sixdata->book;
745 : :
746 : : /* We're dealing with a pre-200107<near-end-of-month> rgmerk
747 : : change re: storing template accounts. */
748 : : /* Fix: get account with name of our GncGUID from the template
749 : : accounts. Make that our template_acct pointer. */
750 : 0 : guid_to_string_buff (xaccSchedXactionGetGUID (sx), guidstr);
751 : 0 : ra = gnc_book_get_template_root (book);
752 : 0 : if (ra == NULL)
753 : : {
754 : 0 : g_warning ("Error getting template root account from being-parsed Book.");
755 : 0 : xmlFreeNode (tree);
756 : 0 : return FALSE;
757 : : }
758 : 0 : acct = gnc_account_lookup_by_name (ra, guidstr);
759 : 0 : if (acct == NULL)
760 : : {
761 : 0 : g_warning ("no template account with name [%s]", guidstr);
762 : 0 : xmlFreeNode (tree);
763 : 0 : return FALSE;
764 : : }
765 : 0 : DEBUG ("template account name [%s] for SX with GncGUID [%s]",
766 : : xaccAccountGetName (acct), guidstr);
767 : :
768 : : /* FIXME: free existing template account.
769 : : * HUH????? We only execute this if there isn't
770 : : * currently an existing template account, don't we?
771 : : * <rgmerk>
772 : : */
773 : :
774 : 0 : sx->template_acct = acct;
775 : : }
776 : :
777 : 5 : done:
778 : 5 : xmlFreeNode (tree);
779 : :
780 : 5 : return successful;
781 : : }
782 : :
783 : : sixtp*
784 : 42 : gnc_schedXaction_sixtp_parser_create (void)
785 : : {
786 : 42 : return sixtp_dom_parser_new (gnc_schedXaction_end_handler, NULL, NULL);
787 : : }
788 : :
789 : : static
790 : : gboolean
791 : 8 : tt_act_handler (xmlNodePtr node, gpointer data)
792 : : {
793 : 8 : gnc_template_xaction_data* txd = static_cast<decltype (txd)> (data);
794 : : Account* acc;
795 : : gnc_commodity* com;
796 : :
797 : 8 : acc = dom_tree_to_account (node, txd->book);
798 : :
799 : 8 : if (acc == NULL)
800 : : {
801 : 0 : return FALSE;
802 : : }
803 : : else
804 : : {
805 : 8 : xaccAccountBeginEdit (acc);
806 : :
807 : : /* Check for the lack of a commodity [signifying that the
808 : : pre-7/11/2001-CIT-change SX template Account was parsed [but
809 : : incorrectly]. */
810 : 8 : if (xaccAccountGetCommodity (acc) == NULL)
811 : : {
812 : : #if 1
813 : : gnc_commodity_table* table;
814 : :
815 : 1 : table = gnc_commodity_table_get_table (txd->book);
816 : 1 : com = gnc_commodity_table_lookup (table,
817 : : GNC_COMMODITY_NS_TEMPLATE, "template");
818 : : #else
819 : : /* FIXME: This should first look in the table of the
820 : : book, maybe? The right thing happens [WRT file
821 : : load/save] if we just _new all the time, but it
822 : : doesn't seem right. This whole block should go
823 : : away at some point, but the same concern still
824 : : applies for
825 : : SchedXaction.c:xaccSchedXactionInit... */
826 : : com = gnc_commodity_new (txd->book,
827 : : "template", GNC_COMMODITY_NS_TEMPLATE,
828 : : "template", "template",
829 : : 1);
830 : : #endif
831 : 1 : xaccAccountSetCommodity (acc, com);
832 : : }
833 : :
834 : 8 : txd->accts = g_list_append (txd->accts, acc);
835 : : }
836 : :
837 : 8 : return TRUE;
838 : : }
839 : :
840 : : static
841 : : gboolean
842 : 5 : tt_trn_handler (xmlNodePtr node, gpointer data)
843 : : {
844 : 5 : gnc_template_xaction_data* txd = static_cast<decltype (txd)> (data);
845 : : Transaction* trn;
846 : :
847 : 5 : trn = dom_tree_to_transaction (node, txd->book);
848 : :
849 : 5 : if (trn == NULL)
850 : : {
851 : 0 : return FALSE;
852 : : }
853 : : else
854 : : {
855 : 5 : txd->transactions = g_list_append (txd->transactions, trn);
856 : : }
857 : :
858 : 5 : return TRUE;
859 : : }
860 : :
861 : : struct dom_tree_handler tt_dom_handlers[] =
862 : : {
863 : : { GNC_ACCOUNT_TAG, tt_act_handler, 0, 0 },
864 : : { GNC_TRANSACTION_TAG, tt_trn_handler, 0, 0 },
865 : : { NULL, NULL, 0, 0 },
866 : : };
867 : :
868 : : static gboolean
869 : 373 : gnc_template_transaction_end_handler (gpointer data_for_children,
870 : : GSList* data_from_children,
871 : : GSList* sibling_data,
872 : : gpointer parent_data,
873 : : gpointer global_data,
874 : : gpointer* result,
875 : : const gchar* tag)
876 : : {
877 : 373 : gboolean successful = FALSE;
878 : 373 : xmlNodePtr tree = static_cast<decltype (tree)> (data_for_children);
879 : 373 : gxpf_data* gdata = static_cast<decltype (gdata)> (global_data);
880 : 373 : QofBook* book = static_cast<decltype (book)> (gdata->bookdata);
881 : : GList* n;
882 : : gnc_template_xaction_data txd;
883 : :
884 : 373 : txd.book = book;
885 : 373 : txd.accts = NULL;
886 : 373 : txd.transactions = NULL;
887 : :
888 : : /* the DOM tree will have an account tree [the template
889 : : accounts] and a list of transactions [which will be members
890 : : of the template account].
891 : :
892 : : we want to parse through the dom trees for each, placing
893 : : the null-parent account in the book's template-group slot,
894 : : the others under it, and the transactions as normal. */
895 : :
896 : 373 : if (parent_data)
897 : : {
898 : 370 : return TRUE;
899 : : }
900 : :
901 : 3 : if (!tag)
902 : : {
903 : 0 : return TRUE;
904 : : }
905 : :
906 : 3 : g_return_val_if_fail (tree, FALSE);
907 : :
908 : 3 : successful = dom_tree_generic_parse (tree, tt_dom_handlers, &txd);
909 : :
910 : 3 : if (successful)
911 : : {
912 : 3 : gdata->cb (tag, gdata->parsedata, &txd);
913 : : }
914 : : else
915 : : {
916 : 0 : g_warning ("failed to parse template transaction");
917 : 0 : xmlElemDump (stdout, NULL, tree);
918 : : }
919 : :
920 : : /* cleanup */
921 : 11 : for (n = txd.accts; n; n = n->next)
922 : : {
923 : 8 : n->data = NULL;
924 : : }
925 : 8 : for (n = txd.transactions; n; n = n->next)
926 : : {
927 : 5 : n->data = NULL;
928 : : }
929 : 3 : g_list_free (txd.accts);
930 : 3 : g_list_free (txd.transactions);
931 : :
932 : 3 : xmlFreeNode (tree);
933 : :
934 : 3 : return successful;
935 : : }
936 : :
937 : : sixtp*
938 : 42 : gnc_template_transaction_sixtp_parser_create (void)
939 : : {
940 : 42 : return sixtp_dom_parser_new (gnc_template_transaction_end_handler,
941 : 42 : NULL, NULL);
942 : : }
|