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