GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gnc-pricedb-xml-v2.c
1 /********************************************************************
2  * gnc-pricedb-xml-v2.c -- xml routines for price db *
3  * Copyright (C) 2001 Gnumatic, Inc. *
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 [email protected] *
21  * *
22  *******************************************************************/
23 
24 #include "config.h"
25 
26 #include <string.h>
27 
28 #include "gnc-xml.h"
29 #include "sixtp.h"
30 #include "sixtp-utils.h"
31 #include "sixtp-parsers.h"
32 #include "sixtp-dom-parsers.h"
33 #include "sixtp-dom-generators.h"
34 #include "io-gncxml-gen.h"
35 #include "io-gncxml-v2.h"
36 
37 #include "gnc-pricedb.h"
38 #include "gnc-pricedb-p.h"
39 
40 /* This static indicates the debugging module that this .o belongs to. */
41 static QofLogModule log_module = GNC_MOD_IO;
42 
43 /* Read and Write the pricedb as XML -- something like this:
44 
45  <pricedb>
46  price-1
47  price-2
48  ...
49  </pricedb>
50 
51  where each price should look roughly like this:
52 
53  <price>
54  <price:id>
55  00000000111111112222222233333333
56  </price:id>
57  <price:commodity>
58  <cmdty:space>NASDAQ</cmdty:space>
59  <cmdty:id>RHAT</cmdty:id>
60  </price:commodity>
61  <price:currency>
62  <cmdty:space>ISO?</cmdty:space>
63  <cmdty:id>USD</cmdty:id>
64  </price:currency>
65  <price:time><ts:date>Mon ...</ts:date><ts:ns>12</ts:ns></price:time>
66  <price:source>Finance::Quote</price:source>
67  <price:type>bid</price:type>
68  <price:value>11011/100</price:value>
69  </price>
70 
71 */
72 
73 /***********************************************************************/
74 /* READING */
75 /***********************************************************************/
76 
77 /****************************************************************************/
78 /* <price>
79 
80  restores a price. Does so via a walk of the XML tree in memory.
81  Returns a GNCPrice * in result.
82 
83  Right now, a price is legitimate even if all of it's fields are not
84  set. We may need to change that later, but at the moment.
85 
86 */
87 
88 static gboolean
89 price_parse_xml_sub_node(GNCPrice *p, xmlNodePtr sub_node, QofBook *book)
90 {
91  if (!p || !sub_node) return FALSE;
92 
93  gnc_price_begin_edit (p);
94  if (g_strcmp0("price:id", (char*)sub_node->name) == 0)
95  {
96  GncGUID *c = dom_tree_to_guid(sub_node);
97  if (!c) return FALSE;
98  gnc_price_set_guid(p, c);
99  g_free(c);
100  }
101  else if (g_strcmp0("price:commodity", (char*)sub_node->name) == 0)
102  {
103  gnc_commodity *c = dom_tree_to_commodity_ref(sub_node, book);
104  if (!c) return FALSE;
105  gnc_price_set_commodity(p, c);
106  }
107  else if (g_strcmp0("price:currency", (char*)sub_node->name) == 0)
108  {
109  gnc_commodity *c = dom_tree_to_commodity_ref(sub_node, book);
110  if (!c) return FALSE;
111  gnc_price_set_currency(p, c);
112  }
113  else if (g_strcmp0("price:time", (char*)sub_node->name) == 0)
114  {
115  Timespec t = dom_tree_to_timespec(sub_node);
116  if (!dom_tree_valid_timespec(&t, sub_node->name)) return FALSE;
117  gnc_price_set_time(p, t);
118  }
119  else if (g_strcmp0("price:source", (char*)sub_node->name) == 0)
120  {
121  char *text = dom_tree_to_text(sub_node);
122  if (!text) return FALSE;
123  gnc_price_set_source(p, text);
124  g_free(text);
125  }
126  else if (g_strcmp0("price:type", (char*)sub_node->name) == 0)
127  {
128  char *text = dom_tree_to_text(sub_node);
129  if (!text) return FALSE;
130  gnc_price_set_typestr(p, text);
131  g_free(text);
132  }
133  else if (g_strcmp0("price:value", (char*)sub_node->name) == 0)
134  {
135  gnc_numeric *value = dom_tree_to_gnc_numeric(sub_node);
136  if (!value) return FALSE;
137  gnc_price_set_value(p, *value);
138  g_free(value);
139  }
140  gnc_price_commit_edit (p);
141  return TRUE;
142 }
143 
144 static gboolean
145 price_parse_xml_end_handler(gpointer data_for_children,
146  GSList* data_from_children,
147  GSList* sibling_data,
148  gpointer parent_data,
149  gpointer global_data,
150  gpointer *result,
151  const gchar *tag)
152 {
153  gboolean ok = TRUE;
154  xmlNodePtr price_xml = (xmlNodePtr) data_for_children;
155  xmlNodePtr child;
156  GNCPrice *p = NULL;
157  gxpf_data *gdata = global_data;
158  QofBook *book = gdata->bookdata;
159 
160  /* we haven't been handed the *top* level node yet... */
161  if (parent_data) return TRUE;
162 
163  *result = NULL;
164 
165  if (!price_xml) return FALSE;
166  if (price_xml->next)
167  {
168  ok = FALSE;
169  goto cleanup_and_exit;
170  }
171  if (price_xml->prev)
172  {
173  ok = FALSE;
174  goto cleanup_and_exit;
175  }
176  if (!price_xml->xmlChildrenNode)
177  {
178  ok = FALSE;
179  goto cleanup_and_exit;
180  }
181 
182  p = gnc_price_create(book);
183  if (!p)
184  {
185  ok = FALSE;
186  goto cleanup_and_exit;
187  }
188 
189  for (child = price_xml->xmlChildrenNode; child; child = child->next)
190  {
191  switch (child->type)
192  {
193  case XML_COMMENT_NODE:
194  case XML_TEXT_NODE:
195  break;
196  case XML_ELEMENT_NODE:
197  if (!price_parse_xml_sub_node(p, child, book))
198  {
199  ok = FALSE;
200  goto cleanup_and_exit;
201  }
202  break;
203  default:
204  PERR("Unknown node type (%d) while parsing gnc-price xml.", child->type);
205  child = NULL;
206  ok = FALSE;
207  goto cleanup_and_exit;
208  break;
209  }
210  }
211 
212 cleanup_and_exit:
213  if (ok)
214  {
215  *result = p;
216  }
217  else
218  {
219  *result = NULL;
220  gnc_price_unref(p);
221  }
222  xmlFreeNode(price_xml);
223  return ok;
224 }
225 
226 static void
227 cleanup_gnc_price(sixtp_child_result *result)
228 {
229  if (result->data) gnc_price_unref((GNCPrice *) result->data);
230 }
231 
232 static sixtp *
233 gnc_price_parser_new (void)
234 {
235  return sixtp_dom_parser_new(price_parse_xml_end_handler,
236  cleanup_gnc_price,
237  cleanup_gnc_price);
238 }
239 
240 
241 /****************************************************************************/
242 /* <pricedb> (lineage <ledger-data>)
243 
244  restores a pricedb. We allocate the new db in the start block, the
245  children add to it, and it gets returned in result. Note that the
246  cleanup handler will destroy the pricedb, so the parent needs to
247  stop that if desired.
248 
249  result: GNCPriceDB*
250 
251  start: create new GNCPriceDB*, and leave in *data_for_children.
252  cleanup-result: destroy GNCPriceDB*
253  result-fail: destroy GNCPriceDB*
254 
255 */
256 
257 static gboolean
258 pricedb_start_handler(GSList* sibling_data,
259  gpointer parent_data,
260  gpointer global_data,
261  gpointer *data_for_children,
262  gpointer *result,
263  const gchar *tag,
264  gchar **attrs)
265 {
266  gxpf_data *gdata = global_data;
267  QofBook *book = gdata->bookdata;
268  GNCPriceDB *db = gnc_pricedb_get_db(book);
269  g_return_val_if_fail(db, FALSE);
270  gnc_pricedb_set_bulk_update(db, TRUE);
271  *result = db;
272  return(TRUE);
273 }
274 
275 static gboolean
276 pricedb_after_child_handler(gpointer data_for_children,
277  GSList* data_from_children,
278  GSList* sibling_data,
279  gpointer parent_data,
280  gpointer global_data,
281  gpointer *result,
282  const gchar *tag,
283  const gchar *child_tag,
284  sixtp_child_result *child_result)
285 {
286  gxpf_data *gdata = global_data;
287  sixtp_gdv2 *gd = gdata->parsedata;
288  GNCPriceDB *db = (GNCPriceDB *) * result;
289 
290  g_return_val_if_fail(db, FALSE);
291 
292  /* right now children have to produce results :> */
293  if (!child_result) return(FALSE);
294  if (child_result->type != SIXTP_CHILD_RESULT_NODE) return(FALSE);
295 
296  if (strcmp(child_result->tag, "price") == 0)
297  {
298  GNCPrice *p = (GNCPrice *) child_result->data;
299 
300  g_return_val_if_fail(p, FALSE);
301  gnc_pricedb_add_price(db, p);
302  gd->counter.prices_loaded++;
303  run_callback(gd, "prices");
304  return TRUE;
305  }
306  else
307  {
308  PERR ("unexpected tag %s\n", child_result->tag);
309  return FALSE;
310  }
311  return FALSE;
312 }
313 
314 static void
315 pricedb_cleanup_result_handler(sixtp_child_result *result)
316 {
317  if (result->data)
318  {
319  GNCPriceDB *db = (GNCPriceDB *) result->data;
320  if (db) gnc_pricedb_destroy(db);
321  result->data = NULL;
322  }
323 }
324 
325 static gboolean
326 pricedb_v2_end_handler(
327  gpointer data_for_children, GSList* data_from_children,
328  GSList* sibling_data, gpointer parent_data, gpointer global_data,
329  gpointer *result, const gchar *tag)
330 {
331  GNCPriceDB *db = *result;
332  gxpf_data *gdata = (gxpf_data*)global_data;
333 
334  if (parent_data)
335  {
336  return TRUE;
337  }
338 
339  if (!tag)
340  {
341  return TRUE;
342  }
343 
344  gdata->cb(tag, gdata->parsedata, db);
345  *result = NULL;
346 
347  gnc_pricedb_set_bulk_update(db, FALSE);
348 
349  return TRUE;
350 }
351 
352 static sixtp*
353 gnc_pricedb_parser_new(void)
354 {
355  sixtp *top_level;
356  sixtp *price_parser;
357 
358  top_level =
359  sixtp_set_any(sixtp_new(), TRUE,
360  SIXTP_START_HANDLER_ID, pricedb_start_handler,
361  SIXTP_AFTER_CHILD_HANDLER_ID, pricedb_after_child_handler,
362  SIXTP_CHARACTERS_HANDLER_ID,
363  allow_and_ignore_only_whitespace,
364  SIXTP_RESULT_FAIL_ID, pricedb_cleanup_result_handler,
365  SIXTP_CLEANUP_RESULT_ID, pricedb_cleanup_result_handler,
366  SIXTP_NO_MORE_HANDLERS);
367 
368  if (!top_level) return NULL;
369 
370  price_parser = gnc_price_parser_new();
371 
372  if (!price_parser)
373  {
374  sixtp_destroy(top_level);
375  return NULL;
376  }
377 
378  sixtp_add_sub_parser(top_level, "price", price_parser);
379 
380  return top_level;
381 }
382 
383 sixtp*
384 gnc_pricedb_sixtp_parser_create(void)
385 {
386  sixtp *ret;
387  ret = gnc_pricedb_parser_new();
388  sixtp_set_end(ret, pricedb_v2_end_handler);
389  return ret;
390 }
391 
392 
393 /***********************************************************************/
394 /* WRITING */
395 /***********************************************************************/
396 
397 static gboolean
398 add_child_or_kill_parent(xmlNodePtr parent, xmlNodePtr child)
399 {
400  if (!child)
401  {
402  xmlFreeNode(parent);
403  return FALSE;
404  }
405  xmlAddChild(parent, child);
406  return TRUE;
407 }
408 
409 static xmlNodePtr
410 gnc_price_to_dom_tree(const xmlChar *tag, GNCPrice *price)
411 {
412  xmlNodePtr price_xml;
413  const gchar *typestr, *sourcestr;
414  xmlNodePtr tmpnode;
415  gnc_commodity *commodity;
416  gnc_commodity *currency;
417  Timespec timesp;
418  gnc_numeric value;
419 
420  if (!(tag && price)) return NULL;
421 
422  price_xml = xmlNewNode(NULL, tag);
423  if (!price_xml) return NULL;
424 
425  commodity = gnc_price_get_commodity(price);
426  currency = gnc_price_get_currency(price);
427 
428  if (!(commodity && currency)) return NULL;
429 
430  tmpnode = guid_to_dom_tree("price:id", gnc_price_get_guid(price));
431  if (!add_child_or_kill_parent(price_xml, tmpnode)) return NULL;
432 
433  tmpnode = commodity_ref_to_dom_tree("price:commodity", commodity);
434  if (!add_child_or_kill_parent(price_xml, tmpnode)) return NULL;
435 
436  tmpnode = commodity_ref_to_dom_tree("price:currency", currency);
437  if (!add_child_or_kill_parent(price_xml, tmpnode)) return NULL;
438 
439  timesp = gnc_price_get_time(price);
440  tmpnode = timespec_to_dom_tree("price:time", &timesp);
441  if (!add_child_or_kill_parent(price_xml, tmpnode)) return NULL;
442 
443  sourcestr = gnc_price_get_source(price);
444  if (sourcestr && (strlen(sourcestr) != 0))
445  {
446  tmpnode = text_to_dom_tree("price:source", sourcestr);
447  if (!add_child_or_kill_parent(price_xml, tmpnode)) return NULL;
448  }
449 
450  typestr = gnc_price_get_typestr(price);
451  if (typestr && (strlen(typestr) != 0))
452  {
453  tmpnode = text_to_dom_tree("price:type", typestr);
454  if (!add_child_or_kill_parent(price_xml, tmpnode)) return NULL;
455  }
456 
457  value = gnc_price_get_value(price);
458  tmpnode = gnc_numeric_to_dom_tree("price:value", &value);
459  if (!add_child_or_kill_parent(price_xml, tmpnode)) return NULL;
460 
461  return price_xml;
462 }
463 
464 static gboolean
465 xml_add_gnc_price_adapter(GNCPrice *p, gpointer data)
466 {
467  xmlNodePtr xml_node = (xmlNodePtr) data;
468 
469  if (p)
470  {
471  xmlNodePtr price_xml = gnc_price_to_dom_tree(BAD_CAST "price", p);
472  if (!price_xml) return FALSE;
473  xmlAddChild(xml_node, price_xml);
474  return TRUE;
475  }
476  else
477  {
478  return TRUE;
479  }
480 }
481 
482 static xmlNodePtr
483 gnc_pricedb_to_dom_tree(const xmlChar *tag, GNCPriceDB *db)
484 {
485  xmlNodePtr db_xml = NULL;
486 
487  if (!tag) return NULL;
488 
489  db_xml = xmlNewNode(NULL, tag);
490  if (!db_xml) return NULL;
491 
492  xmlSetProp(db_xml, BAD_CAST "version", BAD_CAST "1");
493 
494  if (!gnc_pricedb_foreach_price(db, xml_add_gnc_price_adapter, db_xml, TRUE))
495  {
496  xmlFreeNode(db_xml);
497  return NULL;
498  }
499 
500  /* if no children have been added just return NULL */
501  if (!db_xml->xmlChildrenNode)
502  {
503  xmlFreeNode(db_xml);
504  return NULL;
505  }
506 
507  return db_xml;
508 }
509 
510 xmlNodePtr
511 gnc_pricedb_dom_tree_create(GNCPriceDB *db)
512 {
513  return gnc_pricedb_to_dom_tree(BAD_CAST "gnc:pricedb", db);
514 }
GNCPrice * gnc_price_create(QofBook *book)
Definition: gnc-pricedb.c:236
Definition: sixtp.h:93
a simple price database for gnucash
void gnc_price_unref(GNCPrice *p)
Definition: gnc-pricedb.c:272
gboolean gnc_pricedb_add_price(GNCPriceDB *db, GNCPrice *p)
Definition: gnc-pricedb.c:1053
Use a 64-bit unsigned int timespec.
Definition: gnc-date.h:299
#define PERR(format, args...)
Definition: qoflog.h:237
GNCPriceDB * gnc_pricedb_get_db(QofBook *book)
Definition: gnc-pricedb.c:872
Definition: guid.h:65
api for GnuCash version 2 XML-based file format
void gnc_pricedb_destroy(GNCPriceDB *db)
Definition: gnc-pricedb.c:828
gboolean gnc_pricedb_foreach_price(GNCPriceDB *db, gboolean(*f)(GNCPrice *p, gpointer user_data), gpointer user_data, gboolean stable_order)
Definition: gnc-pricedb.c:2344
void gnc_pricedb_set_bulk_update(GNCPriceDB *db, gboolean bulk_update)
Definition: gnc-pricedb.c:844
void run_callback(sixtp_gdv2 *data, const char *type)
Definition: io-gncxml-v2.c:109
const gchar * QofLogModule
Definition: qofid.h:89