GnuCash  5.6-150-g038405b370+
gnc-freqspec-xml-v2.cpp
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 
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 
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 
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 fspd_init (fsParseData* fspd)
113 {
114  fspd->list = NULL;
115  fspd->book = NULL;
116  fspd->recurrence = g_new0 (Recurrence, 1);
117  fspd->recurrence_list = NULL;
118  fspd->uift = UIFREQ_NONE;
119  fspd->interval
120  = fspd->offset
121  = fspd->day
122  = fspd->occurrence
123  = 0;
124  fspd->weekend_adj = WEEKEND_ADJ_NONE;
125  g_date_clear (&fspd->once_day, 1);
126 }
127 
128 static
129 gboolean
130 gnc_fs_handler (xmlNodePtr node, gpointer d);
131 
132 static
133 gboolean
134 fs_uift_handler (xmlNodePtr node, gpointer data)
135 {
136  fsParseData* fspd = static_cast<decltype (fspd)> (data);
137  int i;
138  char* nodeTxt;
139 
140  nodeTxt = dom_tree_to_text (node);
141 
142  g_return_val_if_fail (nodeTxt, FALSE);
143  for (i = 0; uiFreqTypeStrs[i].str != NULL; i++)
144  {
145  if (g_strcmp0 (nodeTxt, uiFreqTypeStrs[i].str) == 0)
146  {
147  fspd->uift = uiFreqTypeStrs[i].uift;
148  g_free (nodeTxt);
149  return TRUE;
150  }
151  }
152  g_free (nodeTxt);
153  return FALSE;
154 }
155 
156 static
157 gboolean
158 fs_date_handler (xmlNodePtr node, gpointer data)
159 {
160  fsParseData* fspd = static_cast<decltype (fspd)> (data);
161  GDate* foo;
162  foo = dom_tree_to_gdate (node);
163  if (foo == NULL)
164  return FALSE;
165  fspd->once_day = *foo;
166  g_date_free (foo);
167  return TRUE;
168 }
169 
170 static
171 gboolean
172 fs_interval_handler (xmlNodePtr node, gpointer data)
173 {
174  fsParseData* fspd = static_cast<decltype (fspd)> (data);
175  gboolean ret;
176  gint64 foo;
177 
178  ret = dom_tree_to_integer (node, &foo);
179  if (! ret)
180  {
181  return ret;
182  }
183  fspd->interval = foo;
184  return TRUE;
185 }
186 
187 static
188 gboolean
189 fs_offset_handler (xmlNodePtr node, gpointer data)
190 {
191  fsParseData* fspd = static_cast<decltype (fspd)> (data);
192  gboolean ret;
193  gint64 foo;
194 
195  ret = dom_tree_to_integer (node, &foo);
196  if (! ret)
197  return ret;
198  fspd->offset = foo;
199  return TRUE;
200 }
201 
202 static
203 gboolean
204 fs_day_handler (xmlNodePtr node, gpointer data)
205 {
206  fsParseData* fspd = static_cast<decltype (fspd)> (data);
207  gboolean ret;
208  gint64 foo;
209 
210  ret = dom_tree_to_integer (node, &foo);
211  if (! ret)
212  return ret;
213  fspd->day = foo;
214  return TRUE;
215 }
216 
217 static
218 gboolean
219 fs_weekday_handler (xmlNodePtr node, gpointer data)
220 {
221  fsParseData* fspd = static_cast<decltype (fspd)> (data);
222  gboolean ret;
223  gint64 foo;
224  ret = dom_tree_to_integer (node, &foo);
225  if (!ret)
226  return ret;
227  fspd->day = foo;
228  return TRUE;
229 }
230 
231 static
232 gboolean
233 fs_occurrence_handler (xmlNodePtr node, gpointer data)
234 {
235  fsParseData* fspd = static_cast<decltype (fspd)> (data);
236  gboolean ret;
237  gint64 foo;
238  ret = dom_tree_to_integer (node, &foo);
239  if (!ret)
240  return ret;
241  fspd->occurrence = foo;
242  return TRUE;
243 }
244 
245 static
246 gboolean
247 fs_weekend_adj_handler (xmlNodePtr node, gpointer data)
248 {
249  fsParseData* fspd = static_cast<decltype (fspd)> (data);
250  gboolean ret;
251  gint64 foo;
252  ret = dom_tree_to_integer (node, &foo);
253  if (!ret)
254  return ret;
255  fspd->weekend_adj = foo;
256  return TRUE;
257 }
258 
259 static
260 gboolean
261 fs_subelement_handler (xmlNodePtr node, gpointer data)
262 {
263  fsParseData* fspd = static_cast<decltype (fspd)> (data);
264  GList* recurrences;
265 
266  recurrences = dom_tree_freqSpec_to_recurrences (node, fspd->book);
267  if (recurrences == NULL)
268  return FALSE;
269 
270  {
271  GList* r_iter;
272  for (r_iter = recurrences; r_iter != NULL; r_iter = r_iter->next)
273  {
274  Recurrence* r = (Recurrence*)r_iter->data;
275  GDate recurrence_date;
276  if (fspd->uift == UIFREQ_SEMI_MONTHLY)
277  {
278  // complementry hack around 'once' freqspects not being valid. :/
279  recurrence_date = recurrenceGetDate (r);
280  recurrenceSet (r, recurrenceGetMultiplier (r), PERIOD_MONTH, &recurrence_date,
281  recurrenceGetWeekendAdjust (r));
282  }
283  fspd->recurrence_list = g_list_append (fspd->recurrence_list, r);
284  }
285  }
286  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 fs_none_handler (xmlNodePtr node, gpointer data)
304 {
305  fsParseData* fspd = static_cast<decltype (fspd)> (data);
306  gboolean successful;
307  successful = dom_tree_generic_parse (node,
308  fs_union_dom_handlers,
309  fspd);
310  return successful;
311 }
312 
313 static
314 gboolean
315 fs_once_handler (xmlNodePtr node, gpointer data)
316 {
317  fsParseData* fspd = static_cast<decltype (fspd)> (data);
318  gboolean successful;
319 
320  successful = dom_tree_generic_parse (node,
321  fs_union_dom_handlers,
322  fspd);
323  if (!successful)
324  return FALSE;
325  recurrenceSet (fspd->recurrence, 0, PERIOD_ONCE, &fspd->once_day,
326  WEEKEND_ADJ_NONE);
327 
328  return TRUE;
329 }
330 
331 static gboolean
332 fs_daily_handler (xmlNodePtr node, gpointer data)
333 {
334  fsParseData* fspd = static_cast<decltype (fspd)> (data);
335  GDate offset_date;
336  gboolean successful;
337  successful = dom_tree_generic_parse (node, fs_union_dom_handlers, fspd);
338  if (!successful)
339  return FALSE;
340 
341  g_date_clear (&offset_date, 1);
342  g_date_set_julian (&offset_date, fspd->offset == 0 ? 7 : fspd->offset);
343  recurrenceSet (fspd->recurrence, fspd->interval, PERIOD_DAY, &offset_date,
344  WEEKEND_ADJ_NONE);
345 
346  return TRUE;
347 }
348 
349 static
350 gboolean
351 fs_weekly_handler (xmlNodePtr node, gpointer data)
352 {
353  fsParseData* fspd = static_cast<decltype (fspd)> (data);
354  GDate offset_date;
355  gboolean successful;
356  successful = dom_tree_generic_parse (node,
357  fs_union_dom_handlers,
358  fspd);
359  if (!successful)
360  return FALSE;
361 
362  g_date_clear (&offset_date, 1);
363  g_date_set_julian (&offset_date, fspd->offset == 0 ? 7 : fspd->offset);
364  recurrenceSet (fspd->recurrence, fspd->interval, PERIOD_WEEK, &offset_date,
365  WEEKEND_ADJ_NONE);
366 
367  return TRUE;
368 }
369 
370 static
371 gboolean
372 fs_monthly_handler (xmlNodePtr node, gpointer data)
373 {
374  fsParseData* fspd = static_cast<decltype (fspd)> (data);
375  GDate offset_date;
376  gboolean successful;
377  successful = dom_tree_generic_parse (node,
378  fs_union_dom_handlers,
379  fspd);
380  if (!successful)
381  return FALSE;
382 
383 
384  g_date_clear (&offset_date, 1);
385  g_date_set_julian (&offset_date, 1);
386  g_date_add_months (&offset_date, fspd->offset);
387  g_date_set_day (&offset_date, fspd->day);
388  if (fspd->uift == UIFREQ_ONCE)
389  {
390  // hack...
391  recurrenceSet (fspd->recurrence, fspd->interval, PERIOD_ONCE, &offset_date,
392  WEEKEND_ADJ_NONE);
393  }
394  else
395  {
396  recurrenceSet (fspd->recurrence, fspd->interval,
397  PERIOD_MONTH, &offset_date,
398  static_cast<WeekendAdjust> (fspd->weekend_adj));
399  }
400 
401  return successful;
402 }
403 
404 static
405 gboolean
406 fs_month_relative_handler (xmlNodePtr node, gpointer data)
407 {
408  g_critical ("this was never supported, how is it in the datafile?");
409  return FALSE;
410 }
411 
412 static
413 gboolean
414 fs_guid_handler (xmlNodePtr node, gpointer data)
415 {
416  return TRUE;
417 }
418 
419 static
420 gboolean
421 fs_composite_handler (xmlNodePtr node, gpointer data)
422 {
423  fsParseData* fspd = static_cast<decltype (fspd)> (data);
424  gboolean successful;
425  successful = dom_tree_generic_parse (node,
426  fs_union_dom_handlers,
427  fspd);
428  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 gnc_fs_handler (xmlNodePtr node, gpointer d)
449 {
450  return dom_tree_generic_parse (node, fs_dom_handlers, d);
451 }
452 
453 static
454 gboolean
455 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  gboolean successful = FALSE;
462  xmlNodePtr tree = (xmlNodePtr)data_for_children;
463  sixtp_gdv2* globaldata = (sixtp_gdv2*)global_data;
464 
465  fspd_init (&fspd);
466  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  if (parent_data)
471  return TRUE;
472 
473  if (!tag)
474  return TRUE;
475 
476  g_return_val_if_fail (tree, FALSE);
477 
478  successful = dom_tree_generic_parse (tree, fs_dom_handlers, &fspd);
479  if (!successful)
480  {
481  xmlElemDump (stdout, NULL, tree);
482  }
483 
484  xmlFreeNode (tree);
485 
486  return successful;
487 }
488 
489 sixtp*
490 gnc_freqSpec_sixtp_parser_create (void)
491 {
492  return sixtp_dom_parser_new (gnc_freqSpec_end_handler, NULL, NULL);
493 }
494 
495 static void
496 common_parse (fsParseData* fspd, xmlNodePtr node, QofBook* book)
497 {
498  gboolean successful;
499 
500  fspd->book = book;
501  successful = dom_tree_generic_parse (node, fs_dom_handlers, fspd);
502  if (!successful)
503  {
504  xmlElemDump (stdout, NULL, node);
505  }
506 }
507 
508 GList*
509 dom_tree_freqSpec_to_recurrences (xmlNodePtr node, QofBook* book)
510 {
511  fsParseData fspd;
512  fspd_init (&fspd);
513  common_parse (&fspd, node, book);
514  if (fspd.recurrence_list == NULL)
515  {
516  fspd.recurrence_list = g_list_append (fspd.recurrence_list, fspd.recurrence);
517  }
518  return fspd.recurrence_list;
519 }
Repeat twice a year.
Definition: FreqSpec.h:68
Definition: sixtp.h:129
Hmmm...
Definition: FreqSpec.h:43
no frequency
Definition: FreqSpec.h:68
Repeat once each week.
Definition: FreqSpec.h:68
Repeat every day.
Definition: FreqSpec.h:68
Repeat three times a year.
Definition: FreqSpec.h:68
Repeat twice a month.
Definition: FreqSpec.h:68
api for GnuCash version 2 XML-based file format
Repeat twice a week.
Definition: FreqSpec.h:68
Period / Date Frequency Specification.
Just occurs once.
Definition: FreqSpec.h:68
Repeat every quarter.
Definition: FreqSpec.h:68
Struct passed around as user-data when parsing the FreqSpec.
Repeat Monday to Friday, skip weekend.
Definition: FreqSpec.h:68
UIFreqType
The user&#39;s conception of the frequency.
Definition: FreqSpec.h:68
FreqType
Frequency specification.
Definition: FreqSpec.h:43
Repeat once a year.
Definition: FreqSpec.h:68
Scheduled Transactions public handling routines.
Repeat once a month.
Definition: FreqSpec.h:68