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