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 : :
139 : 0 : auto nodeTxt = dom_tree_to_text (node);
140 : :
141 : 0 : g_return_val_if_fail (nodeTxt, FALSE);
142 : 0 : for (i = 0; uiFreqTypeStrs[i].str != NULL; i++)
143 : : {
144 : 0 : if (g_strcmp0 (nodeTxt->c_str(), uiFreqTypeStrs[i].str) == 0)
145 : : {
146 : 0 : fspd->uift = uiFreqTypeStrs[i].uift;
147 : 0 : return TRUE;
148 : : }
149 : : }
150 : 0 : return FALSE;
151 : 0 : }
152 : :
153 : : static
154 : : gboolean
155 : 0 : fs_date_handler (xmlNodePtr node, gpointer data)
156 : : {
157 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
158 : : GDate* foo;
159 : 0 : foo = dom_tree_to_gdate (node);
160 : 0 : if (foo == NULL)
161 : 0 : return FALSE;
162 : 0 : fspd->once_day = *foo;
163 : 0 : g_date_free (foo);
164 : 0 : return TRUE;
165 : : }
166 : :
167 : : static
168 : : gboolean
169 : 0 : fs_interval_handler (xmlNodePtr node, gpointer data)
170 : : {
171 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
172 : : gboolean ret;
173 : : gint64 foo;
174 : :
175 : 0 : ret = dom_tree_to_integer (node, &foo);
176 : 0 : if (! ret)
177 : : {
178 : 0 : return ret;
179 : : }
180 : 0 : fspd->interval = foo;
181 : 0 : return TRUE;
182 : : }
183 : :
184 : : static
185 : : gboolean
186 : 0 : fs_offset_handler (xmlNodePtr node, gpointer data)
187 : : {
188 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
189 : : gboolean ret;
190 : : gint64 foo;
191 : :
192 : 0 : ret = dom_tree_to_integer (node, &foo);
193 : 0 : if (! ret)
194 : 0 : return ret;
195 : 0 : fspd->offset = foo;
196 : 0 : return TRUE;
197 : : }
198 : :
199 : : static
200 : : gboolean
201 : 0 : fs_day_handler (xmlNodePtr node, gpointer data)
202 : : {
203 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
204 : : gboolean ret;
205 : : gint64 foo;
206 : :
207 : 0 : ret = dom_tree_to_integer (node, &foo);
208 : 0 : if (! ret)
209 : 0 : return ret;
210 : 0 : fspd->day = foo;
211 : 0 : return TRUE;
212 : : }
213 : :
214 : : static
215 : : gboolean
216 : 0 : fs_weekday_handler (xmlNodePtr node, gpointer data)
217 : : {
218 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
219 : : gboolean ret;
220 : : gint64 foo;
221 : 0 : ret = dom_tree_to_integer (node, &foo);
222 : 0 : if (!ret)
223 : 0 : return ret;
224 : 0 : fspd->day = foo;
225 : 0 : return TRUE;
226 : : }
227 : :
228 : : static
229 : : gboolean
230 : 0 : fs_occurrence_handler (xmlNodePtr node, gpointer data)
231 : : {
232 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
233 : : gboolean ret;
234 : : gint64 foo;
235 : 0 : ret = dom_tree_to_integer (node, &foo);
236 : 0 : if (!ret)
237 : 0 : return ret;
238 : 0 : fspd->occurrence = foo;
239 : 0 : return TRUE;
240 : : }
241 : :
242 : : static
243 : : gboolean
244 : 0 : fs_weekend_adj_handler (xmlNodePtr node, gpointer data)
245 : : {
246 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
247 : : gboolean ret;
248 : : gint64 foo;
249 : 0 : ret = dom_tree_to_integer (node, &foo);
250 : 0 : if (!ret)
251 : 0 : return ret;
252 : 0 : fspd->weekend_adj = foo;
253 : 0 : return TRUE;
254 : : }
255 : :
256 : : static
257 : : gboolean
258 : 0 : fs_subelement_handler (xmlNodePtr node, gpointer data)
259 : : {
260 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
261 : : GList* recurrences;
262 : :
263 : 0 : recurrences = dom_tree_freqSpec_to_recurrences (node, fspd->book);
264 : 0 : if (recurrences == NULL)
265 : 0 : return FALSE;
266 : :
267 : : {
268 : : GList* r_iter;
269 : 0 : for (r_iter = recurrences; r_iter != NULL; r_iter = r_iter->next)
270 : : {
271 : 0 : Recurrence* r = (Recurrence*)r_iter->data;
272 : : GDate recurrence_date;
273 : 0 : if (fspd->uift == UIFREQ_SEMI_MONTHLY)
274 : : {
275 : : // complementry hack around 'once' freqspects not being valid. :/
276 : 0 : recurrence_date = recurrenceGetDate (r);
277 : 0 : recurrenceSet (r, recurrenceGetMultiplier (r), PERIOD_MONTH, &recurrence_date,
278 : : recurrenceGetWeekendAdjust (r));
279 : : }
280 : 0 : fspd->recurrence_list = g_list_append (fspd->recurrence_list, r);
281 : : }
282 : : }
283 : 0 : return TRUE;
284 : : }
285 : :
286 : : struct dom_tree_handler fs_union_dom_handlers[] =
287 : : {
288 : : { "fs:date", fs_date_handler, 0, 0 },
289 : : { "fs:interval", fs_interval_handler, 0, 0 },
290 : : { "fs:offset", fs_offset_handler, 0, 0 },
291 : : { "fs:day", fs_day_handler, 0, 0 },
292 : : { "fs:weekday", fs_weekday_handler, 0, 0 },
293 : : { "fs:occurrence", fs_occurrence_handler, 0, 0 },
294 : : { "fs:weekend_adj", fs_weekend_adj_handler, 0, 0 },
295 : : { "gnc:freqspec", fs_subelement_handler, 0, 0 },
296 : : { NULL, NULL, 0, 0 },
297 : : };
298 : :
299 : : static gboolean
300 : 0 : fs_none_handler (xmlNodePtr node, gpointer data)
301 : : {
302 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
303 : : gboolean successful;
304 : 0 : successful = dom_tree_generic_parse (node,
305 : : fs_union_dom_handlers,
306 : : fspd);
307 : 0 : return successful;
308 : : }
309 : :
310 : : static
311 : : gboolean
312 : 0 : fs_once_handler (xmlNodePtr node, gpointer data)
313 : : {
314 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
315 : : gboolean successful;
316 : :
317 : 0 : successful = dom_tree_generic_parse (node,
318 : : fs_union_dom_handlers,
319 : : fspd);
320 : 0 : if (!successful)
321 : 0 : return FALSE;
322 : 0 : recurrenceSet (fspd->recurrence, 0, PERIOD_ONCE, &fspd->once_day,
323 : : WEEKEND_ADJ_NONE);
324 : :
325 : 0 : return TRUE;
326 : : }
327 : :
328 : : static gboolean
329 : 0 : fs_daily_handler (xmlNodePtr node, gpointer data)
330 : : {
331 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
332 : : GDate offset_date;
333 : : gboolean successful;
334 : 0 : successful = dom_tree_generic_parse (node, fs_union_dom_handlers, fspd);
335 : 0 : if (!successful)
336 : 0 : return FALSE;
337 : :
338 : 0 : g_date_clear (&offset_date, 1);
339 : 0 : g_date_set_julian (&offset_date, fspd->offset == 0 ? 7 : fspd->offset);
340 : 0 : recurrenceSet (fspd->recurrence, fspd->interval, PERIOD_DAY, &offset_date,
341 : : WEEKEND_ADJ_NONE);
342 : :
343 : 0 : return TRUE;
344 : : }
345 : :
346 : : static
347 : : gboolean
348 : 0 : fs_weekly_handler (xmlNodePtr node, gpointer data)
349 : : {
350 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
351 : : GDate offset_date;
352 : : gboolean successful;
353 : 0 : successful = dom_tree_generic_parse (node,
354 : : fs_union_dom_handlers,
355 : : fspd);
356 : 0 : if (!successful)
357 : 0 : return FALSE;
358 : :
359 : 0 : g_date_clear (&offset_date, 1);
360 : 0 : g_date_set_julian (&offset_date, fspd->offset == 0 ? 7 : fspd->offset);
361 : 0 : recurrenceSet (fspd->recurrence, fspd->interval, PERIOD_WEEK, &offset_date,
362 : : WEEKEND_ADJ_NONE);
363 : :
364 : 0 : return TRUE;
365 : : }
366 : :
367 : : static
368 : : gboolean
369 : 0 : fs_monthly_handler (xmlNodePtr node, gpointer data)
370 : : {
371 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
372 : : GDate offset_date;
373 : : gboolean successful;
374 : 0 : successful = dom_tree_generic_parse (node,
375 : : fs_union_dom_handlers,
376 : : fspd);
377 : 0 : if (!successful)
378 : 0 : return FALSE;
379 : :
380 : :
381 : 0 : g_date_clear (&offset_date, 1);
382 : 0 : g_date_set_julian (&offset_date, 1);
383 : 0 : g_date_add_months (&offset_date, fspd->offset);
384 : 0 : g_date_set_day (&offset_date, fspd->day);
385 : 0 : if (fspd->uift == UIFREQ_ONCE)
386 : : {
387 : : // hack...
388 : 0 : recurrenceSet (fspd->recurrence, fspd->interval, PERIOD_ONCE, &offset_date,
389 : : WEEKEND_ADJ_NONE);
390 : : }
391 : : else
392 : : {
393 : 0 : recurrenceSet (fspd->recurrence, fspd->interval,
394 : : PERIOD_MONTH, &offset_date,
395 : 0 : static_cast<WeekendAdjust> (fspd->weekend_adj));
396 : : }
397 : :
398 : 0 : return successful;
399 : : }
400 : :
401 : : static
402 : : gboolean
403 : 0 : fs_month_relative_handler (xmlNodePtr node, gpointer data)
404 : : {
405 : 0 : g_critical ("this was never supported, how is it in the datafile?");
406 : 0 : return FALSE;
407 : : }
408 : :
409 : : static
410 : : gboolean
411 : 0 : fs_guid_handler (xmlNodePtr node, gpointer data)
412 : : {
413 : 0 : return TRUE;
414 : : }
415 : :
416 : : static
417 : : gboolean
418 : 0 : fs_composite_handler (xmlNodePtr node, gpointer data)
419 : : {
420 : 0 : fsParseData* fspd = static_cast<decltype (fspd)> (data);
421 : : gboolean successful;
422 : 0 : successful = dom_tree_generic_parse (node,
423 : : fs_union_dom_handlers,
424 : : fspd);
425 : 0 : return successful;
426 : : }
427 : :
428 : : static struct dom_tree_handler fs_dom_handlers[] =
429 : : {
430 : : { "gnc:freqspec", gnc_fs_handler, 0, 0 },
431 : : { "fs:ui_type", fs_uift_handler, 1, 0 },
432 : : { "fs:id", fs_guid_handler, 1, 0 },
433 : : { "fs:none", fs_none_handler, 0, 0 },
434 : : { "fs:once", fs_once_handler, 0, 0 },
435 : : { "fs:daily", fs_daily_handler, 0, 0 },
436 : : { "fs:weekly", fs_weekly_handler, 0, 0 },
437 : : { "fs:monthly", fs_monthly_handler, 0, 0 },
438 : : { "fs:month_relative", fs_month_relative_handler, 0, 0 },
439 : : { "fs:composite", fs_composite_handler, 0, 0 },
440 : : { NULL, NULL, 0, 0 }
441 : : };
442 : :
443 : : static
444 : : gboolean
445 : 0 : gnc_fs_handler (xmlNodePtr node, gpointer d)
446 : : {
447 : 0 : return dom_tree_generic_parse (node, fs_dom_handlers, d);
448 : : }
449 : :
450 : : static
451 : : gboolean
452 : 0 : gnc_freqSpec_end_handler (gpointer data_for_children,
453 : : GSList* data_from_children, GSList* sibling_data,
454 : : gpointer parent_data, gpointer global_data,
455 : : gpointer* result, const gchar* tag)
456 : : {
457 : : fsParseData fspd;
458 : 0 : gboolean successful = FALSE;
459 : 0 : xmlNodePtr tree = (xmlNodePtr)data_for_children;
460 : 0 : sixtp_gdv2* globaldata = (sixtp_gdv2*)global_data;
461 : :
462 : 0 : fspd_init (&fspd);
463 : 0 : fspd.book = globaldata->book;
464 : :
465 : : /* this won't actually get invoked [FreqSpecs aren't top-level
466 : : elements]; see dom_tree_to_freqSpec(), below. */
467 : 0 : if (parent_data)
468 : 0 : return TRUE;
469 : :
470 : 0 : if (!tag)
471 : 0 : return TRUE;
472 : :
473 : 0 : g_return_val_if_fail (tree, FALSE);
474 : :
475 : 0 : successful = dom_tree_generic_parse (tree, fs_dom_handlers, &fspd);
476 : 0 : if (!successful)
477 : : {
478 : 0 : xmlElemDump (stdout, NULL, tree);
479 : : }
480 : :
481 : 0 : xmlFreeNode (tree);
482 : :
483 : 0 : return successful;
484 : : }
485 : :
486 : : sixtp*
487 : 0 : gnc_freqSpec_sixtp_parser_create (void)
488 : : {
489 : 0 : return sixtp_dom_parser_new (gnc_freqSpec_end_handler, NULL, NULL);
490 : : }
491 : :
492 : : static void
493 : 0 : common_parse (fsParseData* fspd, xmlNodePtr node, QofBook* book)
494 : : {
495 : : gboolean successful;
496 : :
497 : 0 : fspd->book = book;
498 : 0 : successful = dom_tree_generic_parse (node, fs_dom_handlers, fspd);
499 : 0 : if (!successful)
500 : : {
501 : 0 : xmlElemDump (stdout, NULL, node);
502 : : }
503 : 0 : }
504 : :
505 : : GList*
506 : 0 : dom_tree_freqSpec_to_recurrences (xmlNodePtr node, QofBook* book)
507 : : {
508 : : fsParseData fspd;
509 : 0 : fspd_init (&fspd);
510 : 0 : common_parse (&fspd, node, book);
511 : 0 : if (fspd.recurrence_list == NULL)
512 : : {
513 : 0 : fspd.recurrence_list = g_list_append (fspd.recurrence_list, fspd.recurrence);
514 : : }
515 : 0 : return fspd.recurrence_list;
516 : : }
|