Branch data Line data Source code
1 : : /********************************************************************
2 : : * gnc-freqspec-xml-v2.c -- xml routines for FreqSpecs *
3 : : * Copyright (C) 2001 Joshua Sled <jsled@asynchronous.org> *
4 : : * Copyright (C) 2001 Ben Stanley <bds02@uow.edu.au> *
5 : : * *
6 : : * This program is free software; you can redistribute it and/or *
7 : : * modify it under the terms of the GNU General Public License as *
8 : : * published by the Free Software Foundation; either version 2 of *
9 : : * the License, or (at your option) any later version. *
10 : : * *
11 : : * This program is distributed in the hope that it will be useful, *
12 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 : : * GNU General Public License for more details. *
15 : : * *
16 : : * You should have received a copy of the GNU General Public License*
17 : : * along with this program; if not, contact: *
18 : : * *
19 : : * Free Software Foundation Voice: +1-617-542-5942 *
20 : : * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
21 : : * Boston, MA 02110-1301, USA gnu@gnu.org *
22 : : * *
23 : : *******************************************************************/
24 : :
25 : : #include <glib.h>
26 : :
27 : : #include <config.h>
28 : : #include <string.h>
29 : : #include "qof.h"
30 : : #include "SchedXaction.h"
31 : : #include "FreqSpec.h"
32 : :
33 : : #include "gnc-xml-helper.h"
34 : : #include "sixtp.h"
35 : : #include "sixtp-utils.h"
36 : : #include "sixtp-parsers.h"
37 : : #include "sixtp-utils.h"
38 : : #include "sixtp-dom-parsers.h"
39 : : #include "sixtp-dom-generators.h"
40 : :
41 : : #include "gnc-xml.h"
42 : :
43 : : #include "io-gncxml-v2.h"
44 : :
45 : : #include "sixtp-dom-parsers.h"
46 : :
47 : : const gchar* freqspec_version_string = "1.0.0";
48 : :
49 : : struct freqTypeTuple
50 : : {
51 : : const char* str;
52 : : FreqType ft;
53 : : };
54 : :
55 : : struct freqTypeTuple freqTypeStrs[] =
56 : : {
57 : : { "none", INVALID },
58 : : { "once", ONCE },
59 : : { "daily", DAILY },
60 : : { "weekly", WEEKLY },
61 : : { "monthly", MONTHLY },
62 : : { "month_relative", MONTH_RELATIVE },
63 : : { "composite", COMPOSITE },
64 : : { NULL, static_cast<FreqType> (-1) },
65 : : };
66 : :
67 : : struct uiFreqTypeTuple
68 : : {
69 : : const char* str;
70 : : UIFreqType uift;
71 : : };
72 : :
73 : : struct uiFreqTypeTuple uiFreqTypeStrs[] =
74 : : {
75 : : { "none", UIFREQ_NONE },
76 : : { "once", UIFREQ_ONCE },
77 : : { "daily", UIFREQ_DAILY },
78 : : { "daily_mf", UIFREQ_DAILY_MF },
79 : : { "weekly", UIFREQ_WEEKLY },
80 : : { "bi_weekly", UIFREQ_BI_WEEKLY },
81 : : { "semi_monthly", UIFREQ_SEMI_MONTHLY },
82 : : { "monthly", UIFREQ_MONTHLY },
83 : : { "quarterly", UIFREQ_QUARTERLY },
84 : : { "tri_anually", UIFREQ_TRI_ANUALLY },
85 : : { "semi_yearly", UIFREQ_SEMI_YEARLY },
86 : : { "yearly", UIFREQ_YEARLY },
87 : : { NULL, static_cast<UIFreqType> (-1) }
88 : : };
89 : :
90 : : /**
91 : : * Struct passed around as user-data when parsing the FreqSpec.
92 : : **/
93 : : typedef struct
94 : : {
95 : : QofBook* book; /* Book we're loading into. */
96 : :
97 : : Recurrence* recurrence;
98 : : GList* recurrence_list;
99 : :
100 : : /* fields used in the union of unions... :) */
101 : : GDate once_day; /* once */
102 : : gint64 interval; /* all [except once] */
103 : : gint64 offset; /* all [except once] */
104 : : gint64 day; /* monthly or month-relative */
105 : : gint64 occurrence; /* month-relative */
106 : : gint64 weekend_adj; /* monthly/yearly */
107 : : GList* list; /* composite */
108 : : UIFreqType uift;
109 : : } fsParseData;
110 : :
111 : : static void
112 : 0 : fspd_init (fsParseData* fspd)
113 : : {
114 : 0 : fspd->list = NULL;
115 : 0 : fspd->book = NULL;
116 : 0 : fspd->recurrence = g_new0 (Recurrence, 1);
117 : 0 : fspd->recurrence_list = NULL;
118 : 0 : fspd->uift = UIFREQ_NONE;
119 : : fspd->interval
120 : 0 : = fspd->offset
121 : 0 : = fspd->day
122 : 0 : = fspd->occurrence
123 : 0 : = 0;
124 : 0 : fspd->weekend_adj = WEEKEND_ADJ_NONE;
125 : 0 : g_date_clear (&fspd->once_day, 1);
126 : 0 : }
127 : :
128 : : static
129 : : gboolean
130 : : gnc_fs_handler (xmlNodePtr node, gpointer d);
131 : :
132 : : static
133 : : gboolean
134 : 0 : fs_uift_handler (xmlNodePtr node, gpointer data)
135 : : {
136 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
137 : : int i;
138 : : char* nodeTxt;
139 : :
140 : 0 : nodeTxt = dom_tree_to_text (node);
141 : :
142 : 0 : g_return_val_if_fail (nodeTxt, FALSE);
143 : 0 : for (i = 0; uiFreqTypeStrs[i].str != NULL; i++)
144 : : {
145 : 0 : if (g_strcmp0 (nodeTxt, uiFreqTypeStrs[i].str) == 0)
146 : : {
147 : 0 : fspd->uift = uiFreqTypeStrs[i].uift;
148 : 0 : g_free (nodeTxt);
149 : 0 : return TRUE;
150 : : }
151 : : }
152 : 0 : g_free (nodeTxt);
153 : 0 : return FALSE;
154 : : }
155 : :
156 : : static
157 : : gboolean
158 : 0 : fs_date_handler (xmlNodePtr node, gpointer data)
159 : : {
160 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
161 : : GDate* foo;
162 : 0 : foo = dom_tree_to_gdate (node);
163 : 0 : if (foo == NULL)
164 : 0 : return FALSE;
165 : 0 : fspd->once_day = *foo;
166 : 0 : g_date_free (foo);
167 : 0 : return TRUE;
168 : : }
169 : :
170 : : static
171 : : gboolean
172 : 0 : fs_interval_handler (xmlNodePtr node, gpointer data)
173 : : {
174 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
175 : : gboolean ret;
176 : : gint64 foo;
177 : :
178 : 0 : ret = dom_tree_to_integer (node, &foo);
179 : 0 : if (! ret)
180 : : {
181 : 0 : return ret;
182 : : }
183 : 0 : fspd->interval = foo;
184 : 0 : return TRUE;
185 : : }
186 : :
187 : : static
188 : : gboolean
189 : 0 : fs_offset_handler (xmlNodePtr node, gpointer data)
190 : : {
191 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
192 : : gboolean ret;
193 : : gint64 foo;
194 : :
195 : 0 : ret = dom_tree_to_integer (node, &foo);
196 : 0 : if (! ret)
197 : 0 : return ret;
198 : 0 : fspd->offset = foo;
199 : 0 : return TRUE;
200 : : }
201 : :
202 : : static
203 : : gboolean
204 : 0 : fs_day_handler (xmlNodePtr node, gpointer data)
205 : : {
206 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
207 : : gboolean ret;
208 : : gint64 foo;
209 : :
210 : 0 : ret = dom_tree_to_integer (node, &foo);
211 : 0 : if (! ret)
212 : 0 : return ret;
213 : 0 : fspd->day = foo;
214 : 0 : return TRUE;
215 : : }
216 : :
217 : : static
218 : : gboolean
219 : 0 : fs_weekday_handler (xmlNodePtr node, gpointer data)
220 : : {
221 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
222 : : gboolean ret;
223 : : gint64 foo;
224 : 0 : ret = dom_tree_to_integer (node, &foo);
225 : 0 : if (!ret)
226 : 0 : return ret;
227 : 0 : fspd->day = foo;
228 : 0 : return TRUE;
229 : : }
230 : :
231 : : static
232 : : gboolean
233 : 0 : fs_occurrence_handler (xmlNodePtr node, gpointer data)
234 : : {
235 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
236 : : gboolean ret;
237 : : gint64 foo;
238 : 0 : ret = dom_tree_to_integer (node, &foo);
239 : 0 : if (!ret)
240 : 0 : return ret;
241 : 0 : fspd->occurrence = foo;
242 : 0 : return TRUE;
243 : : }
244 : :
245 : : static
246 : : gboolean
247 : 0 : fs_weekend_adj_handler (xmlNodePtr node, gpointer data)
248 : : {
249 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
250 : : gboolean ret;
251 : : gint64 foo;
252 : 0 : ret = dom_tree_to_integer (node, &foo);
253 : 0 : if (!ret)
254 : 0 : return ret;
255 : 0 : fspd->weekend_adj = foo;
256 : 0 : return TRUE;
257 : : }
258 : :
259 : : static
260 : : gboolean
261 : 0 : fs_subelement_handler (xmlNodePtr node, gpointer data)
262 : : {
263 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
264 : : GList* recurrences;
265 : :
266 : 0 : recurrences = dom_tree_freqSpec_to_recurrences (node, fspd->book);
267 : 0 : if (recurrences == NULL)
268 : 0 : return FALSE;
269 : :
270 : : {
271 : : GList* r_iter;
272 : 0 : for (r_iter = recurrences; r_iter != NULL; r_iter = r_iter->next)
273 : : {
274 : 0 : Recurrence* r = (Recurrence*)r_iter->data;
275 : : GDate recurrence_date;
276 : 0 : if (fspd->uift == UIFREQ_SEMI_MONTHLY)
277 : : {
278 : : // complementry hack around 'once' freqspects not being valid. :/
279 : 0 : recurrence_date = recurrenceGetDate (r);
280 : 0 : recurrenceSet (r, recurrenceGetMultiplier (r), PERIOD_MONTH, &recurrence_date,
281 : : recurrenceGetWeekendAdjust (r));
282 : : }
283 : 0 : fspd->recurrence_list = g_list_append (fspd->recurrence_list, r);
284 : : }
285 : : }
286 : 0 : return TRUE;
287 : : }
288 : :
289 : : struct dom_tree_handler fs_union_dom_handlers[] =
290 : : {
291 : : { "fs:date", fs_date_handler, 0, 0 },
292 : : { "fs:interval", fs_interval_handler, 0, 0 },
293 : : { "fs:offset", fs_offset_handler, 0, 0 },
294 : : { "fs:day", fs_day_handler, 0, 0 },
295 : : { "fs:weekday", fs_weekday_handler, 0, 0 },
296 : : { "fs:occurrence", fs_occurrence_handler, 0, 0 },
297 : : { "fs:weekend_adj", fs_weekend_adj_handler, 0, 0 },
298 : : { "gnc:freqspec", fs_subelement_handler, 0, 0 },
299 : : { NULL, NULL, 0, 0 },
300 : : };
301 : :
302 : : static gboolean
303 : 0 : fs_none_handler (xmlNodePtr node, gpointer data)
304 : : {
305 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
306 : : gboolean successful;
307 : 0 : successful = dom_tree_generic_parse (node,
308 : : fs_union_dom_handlers,
309 : : fspd);
310 : 0 : return successful;
311 : : }
312 : :
313 : : static
314 : : gboolean
315 : 0 : fs_once_handler (xmlNodePtr node, gpointer data)
316 : : {
317 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
318 : : gboolean successful;
319 : :
320 : 0 : successful = dom_tree_generic_parse (node,
321 : : fs_union_dom_handlers,
322 : : fspd);
323 : 0 : if (!successful)
324 : 0 : return FALSE;
325 : 0 : recurrenceSet (fspd->recurrence, 0, PERIOD_ONCE, &fspd->once_day,
326 : : WEEKEND_ADJ_NONE);
327 : :
328 : 0 : return TRUE;
329 : : }
330 : :
331 : : static gboolean
332 : 0 : fs_daily_handler (xmlNodePtr node, gpointer data)
333 : : {
334 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
335 : : GDate offset_date;
336 : : gboolean successful;
337 : 0 : successful = dom_tree_generic_parse (node, fs_union_dom_handlers, fspd);
338 : 0 : if (!successful)
339 : 0 : return FALSE;
340 : :
341 : 0 : g_date_clear (&offset_date, 1);
342 : 0 : g_date_set_julian (&offset_date, fspd->offset == 0 ? 7 : fspd->offset);
343 : 0 : recurrenceSet (fspd->recurrence, fspd->interval, PERIOD_DAY, &offset_date,
344 : : WEEKEND_ADJ_NONE);
345 : :
346 : 0 : return TRUE;
347 : : }
348 : :
349 : : static
350 : : gboolean
351 : 0 : fs_weekly_handler (xmlNodePtr node, gpointer data)
352 : : {
353 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
354 : : GDate offset_date;
355 : : gboolean successful;
356 : 0 : successful = dom_tree_generic_parse (node,
357 : : fs_union_dom_handlers,
358 : : fspd);
359 : 0 : if (!successful)
360 : 0 : return FALSE;
361 : :
362 : 0 : g_date_clear (&offset_date, 1);
363 : 0 : g_date_set_julian (&offset_date, fspd->offset == 0 ? 7 : fspd->offset);
364 : 0 : recurrenceSet (fspd->recurrence, fspd->interval, PERIOD_WEEK, &offset_date,
365 : : WEEKEND_ADJ_NONE);
366 : :
367 : 0 : return TRUE;
368 : : }
369 : :
370 : : static
371 : : gboolean
372 : 0 : fs_monthly_handler (xmlNodePtr node, gpointer data)
373 : : {
374 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
375 : : GDate offset_date;
376 : : gboolean successful;
377 : 0 : successful = dom_tree_generic_parse (node,
378 : : fs_union_dom_handlers,
379 : : fspd);
380 : 0 : if (!successful)
381 : 0 : return FALSE;
382 : :
383 : :
384 : 0 : g_date_clear (&offset_date, 1);
385 : 0 : g_date_set_julian (&offset_date, 1);
386 : 0 : g_date_add_months (&offset_date, fspd->offset);
387 : 0 : g_date_set_day (&offset_date, fspd->day);
388 : 0 : if (fspd->uift == UIFREQ_ONCE)
389 : : {
390 : : // hack...
391 : 0 : recurrenceSet (fspd->recurrence, fspd->interval, PERIOD_ONCE, &offset_date,
392 : : WEEKEND_ADJ_NONE);
393 : : }
394 : : else
395 : : {
396 : 0 : recurrenceSet (fspd->recurrence, fspd->interval,
397 : : PERIOD_MONTH, &offset_date,
398 : 0 : static_cast<WeekendAdjust> (fspd->weekend_adj));
399 : : }
400 : :
401 : 0 : return successful;
402 : : }
403 : :
404 : : static
405 : : gboolean
406 : 0 : fs_month_relative_handler (xmlNodePtr node, gpointer data)
407 : : {
408 : 0 : g_critical ("this was never supported, how is it in the datafile?");
409 : 0 : return FALSE;
410 : : }
411 : :
412 : : static
413 : : gboolean
414 : 0 : fs_guid_handler (xmlNodePtr node, gpointer data)
415 : : {
416 : 0 : return TRUE;
417 : : }
418 : :
419 : : static
420 : : gboolean
421 : 0 : fs_composite_handler (xmlNodePtr node, gpointer data)
422 : : {
423 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
424 : : gboolean successful;
425 : 0 : successful = dom_tree_generic_parse (node,
426 : : fs_union_dom_handlers,
427 : : fspd);
428 : 0 : return successful;
429 : : }
430 : :
431 : : static struct dom_tree_handler fs_dom_handlers[] =
432 : : {
433 : : { "gnc:freqspec", gnc_fs_handler, 0, 0 },
434 : : { "fs:ui_type", fs_uift_handler, 1, 0 },
435 : : { "fs:id", fs_guid_handler, 1, 0 },
436 : : { "fs:none", fs_none_handler, 0, 0 },
437 : : { "fs:once", fs_once_handler, 0, 0 },
438 : : { "fs:daily", fs_daily_handler, 0, 0 },
439 : : { "fs:weekly", fs_weekly_handler, 0, 0 },
440 : : { "fs:monthly", fs_monthly_handler, 0, 0 },
441 : : { "fs:month_relative", fs_month_relative_handler, 0, 0 },
442 : : { "fs:composite", fs_composite_handler, 0, 0 },
443 : : { NULL, NULL, 0, 0 }
444 : : };
445 : :
446 : : static
447 : : gboolean
448 : 0 : gnc_fs_handler (xmlNodePtr node, gpointer d)
449 : : {
450 : 0 : return dom_tree_generic_parse (node, fs_dom_handlers, d);
451 : : }
452 : :
453 : : static
454 : : gboolean
455 : 0 : gnc_freqSpec_end_handler (gpointer data_for_children,
456 : : GSList* data_from_children, GSList* sibling_data,
457 : : gpointer parent_data, gpointer global_data,
458 : : gpointer* result, const gchar* tag)
459 : : {
460 : : fsParseData fspd;
461 : 0 : gboolean successful = FALSE;
462 : 0 : xmlNodePtr tree = (xmlNodePtr)data_for_children;
463 : 0 : sixtp_gdv2* globaldata = (sixtp_gdv2*)global_data;
464 : :
465 : 0 : fspd_init (&fspd);
466 : 0 : fspd.book = globaldata->book;
467 : :
468 : : /* this won't actually get invoked [FreqSpecs aren't top-level
469 : : elements]; see dom_tree_to_freqSpec(), below. */
470 : 0 : if (parent_data)
471 : 0 : return TRUE;
472 : :
473 : 0 : if (!tag)
474 : 0 : return TRUE;
475 : :
476 : 0 : g_return_val_if_fail (tree, FALSE);
477 : :
478 : 0 : successful = dom_tree_generic_parse (tree, fs_dom_handlers, &fspd);
479 : 0 : if (!successful)
480 : : {
481 : 0 : xmlElemDump (stdout, NULL, tree);
482 : : }
483 : :
484 : 0 : xmlFreeNode (tree);
485 : :
486 : 0 : return successful;
487 : : }
488 : :
489 : : sixtp*
490 : 0 : gnc_freqSpec_sixtp_parser_create (void)
491 : : {
492 : 0 : return sixtp_dom_parser_new (gnc_freqSpec_end_handler, NULL, NULL);
493 : : }
494 : :
495 : : static void
496 : 0 : common_parse (fsParseData* fspd, xmlNodePtr node, QofBook* book)
497 : : {
498 : : gboolean successful;
499 : :
500 : 0 : fspd->book = book;
501 : 0 : successful = dom_tree_generic_parse (node, fs_dom_handlers, fspd);
502 : 0 : if (!successful)
503 : : {
504 : 0 : xmlElemDump (stdout, NULL, node);
505 : : }
506 : 0 : }
507 : :
508 : : GList*
509 : 0 : dom_tree_freqSpec_to_recurrences (xmlNodePtr node, QofBook* book)
510 : : {
511 : : fsParseData fspd;
512 : 0 : fspd_init (&fspd);
513 : 0 : common_parse (&fspd, node, book);
514 : 0 : if (fspd.recurrence_list == NULL)
515 : : {
516 : 0 : fspd.recurrence_list = g_list_append (fspd.recurrence_list, fspd.recurrence);
517 : : }
518 : 0 : return fspd.recurrence_list;
519 : : }
|