00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include "config.h"
00026
00027 #include <glib.h>
00028 #include <string.h>
00029 #include "gnc-pricedb-p.h"
00030 #include "qofbackend-p.h"
00031
00032
00033 static QofLogModule log_module = GNC_MOD_PRICE;
00034
00035 static gboolean add_price(GNCPriceDB *db, GNCPrice *p);
00036 static gboolean remove_price(GNCPriceDB *db, GNCPrice *p, gboolean cleanup);
00037
00038
00039 QOF_GOBJECT_IMPL(gnc_price, GNCPrice, QOF_TYPE_INSTANCE);
00040
00041 static void
00042 gnc_price_init(GNCPrice* price)
00043 {
00044 }
00045
00046 static void
00047 gnc_price_dispose_real (GObject *pricep)
00048 {
00049 }
00050
00051 static void
00052 gnc_price_finalize_real(GObject* pricep)
00053 {
00054 }
00055
00056
00057
00058
00059
00060
00061 GNCPrice *
00062 gnc_price_create (QofBook *book)
00063 {
00064 GNCPrice *p;
00065
00066 g_return_val_if_fail (book, NULL);
00067
00068 p = g_object_new(GNC_TYPE_PRICE, NULL);
00069
00070 p->refcount = 1;
00071 p->value = gnc_numeric_zero();
00072 p->type = NULL;
00073 p->source = NULL;
00074
00075 qof_instance_init_data (&p->inst, GNC_ID_PRICE, book);
00076 qof_event_gen (&p->inst, QOF_EVENT_CREATE, NULL);
00077
00078 return p;
00079 }
00080
00081 static void
00082 gnc_price_destroy (GNCPrice *p)
00083 {
00084 ENTER(" ");
00085 qof_event_gen (&p->inst, QOF_EVENT_DESTROY, NULL);
00086
00087 if(p->type) CACHE_REMOVE(p->type);
00088 if(p->source) CACHE_REMOVE(p->source);
00089
00090
00091 g_object_unref(p);
00092 LEAVE (" ");
00093 }
00094
00095 void
00096 gnc_price_ref(GNCPrice *p)
00097 {
00098 if(!p) return;
00099 p->refcount++;
00100 }
00101
00102 void
00103 gnc_price_unref(GNCPrice *p)
00104 {
00105 if(!p) return;
00106 if(p->refcount == 0) {
00107 return;
00108 }
00109
00110 p->refcount--;
00111
00112 if(p->refcount <= 0) {
00113 if (NULL != p->db) {
00114 PERR("last unref while price in database");
00115 }
00116 gnc_price_destroy (p);
00117 }
00118 }
00119
00120
00121
00122 GNCPrice *
00123 gnc_price_clone (GNCPrice* p, QofBook *book)
00124 {
00125
00126 GNCPrice *new_p;
00127
00128 g_return_val_if_fail (book, NULL);
00129
00130 ENTER ("pr=%p", p);
00131
00132 if(!p) { LEAVE (" "); return NULL; }
00133
00134 new_p = gnc_price_create(book);
00135 if(!new_p) { LEAVE (" "); return NULL; }
00136
00137 qof_instance_copy_version(new_p, p);
00138
00139 gnc_price_begin_edit(new_p);
00140
00141 gnc_price_set_commodity(new_p, gnc_price_get_commodity(p));
00142 gnc_price_set_time(new_p, gnc_price_get_time(p));
00143 gnc_price_set_source(new_p, gnc_price_get_source(p));
00144 gnc_price_set_typestr(new_p, gnc_price_get_typestr(p));
00145 gnc_price_set_value(new_p, gnc_price_get_value(p));
00146 gnc_price_set_currency(new_p, gnc_price_get_currency(p));
00147 gnc_price_commit_edit(new_p);
00148 LEAVE (" ");
00149 return(new_p);
00150 }
00151
00152
00153
00154 void
00155 gnc_price_begin_edit (GNCPrice *p)
00156 {
00157 qof_begin_edit(&p->inst);
00158 }
00159
00160 static void commit_err (QofInstance *inst, QofBackendError errcode)
00161 {
00162 PERR ("Failed to commit: %d", errcode);
00163 }
00164
00165 static void noop (QofInstance *inst) {}
00166
00167 void
00168 gnc_price_commit_edit (GNCPrice *p)
00169 {
00170 if (!qof_commit_edit (QOF_INSTANCE(p))) return;
00171 qof_commit_edit_part2 (&p->inst, commit_err, noop, noop);
00172 }
00173
00174
00175
00176 void
00177 gnc_pricedb_begin_edit (GNCPriceDB *pdb)
00178 {
00179 qof_begin_edit(&pdb->inst);
00180 }
00181
00182 void
00183 gnc_pricedb_commit_edit (GNCPriceDB *pdb)
00184 {
00185 if (!qof_commit_edit (QOF_INSTANCE(pdb))) return;
00186 qof_commit_edit_part2 (&pdb->inst, commit_err, noop, noop);
00187 }
00188
00189
00190
00191
00192 static void
00193 gnc_price_set_dirty (GNCPrice *p)
00194 {
00195 qof_instance_set_dirty(&p->inst);
00196 qof_event_gen(&p->inst, QOF_EVENT_MODIFY, NULL);
00197 }
00198
00199 void
00200 gnc_price_set_commodity(GNCPrice *p, gnc_commodity *c)
00201 {
00202 if(!p) return;
00203
00204 if(!gnc_commodity_equiv(p->commodity, c))
00205 {
00206
00207
00208
00209 gnc_price_ref (p);
00210 remove_price (p->db, p, TRUE);
00211 gnc_price_begin_edit (p);
00212 p->commodity = c;
00213 gnc_price_set_dirty(p);
00214 gnc_price_commit_edit (p);
00215 add_price (p->db, p);
00216 gnc_price_unref (p);
00217 }
00218 }
00219
00220
00221 void
00222 gnc_price_set_currency(GNCPrice *p, gnc_commodity *c)
00223 {
00224 if(!p) return;
00225
00226 if(!gnc_commodity_equiv(p->currency, c))
00227 {
00228
00229
00230
00231 gnc_price_ref (p);
00232 remove_price (p->db, p, TRUE);
00233 gnc_price_begin_edit (p);
00234 p->currency = c;
00235 gnc_price_set_dirty(p);
00236 gnc_price_commit_edit (p);
00237 add_price (p->db, p);
00238 gnc_price_unref (p);
00239 }
00240 }
00241
00242 void
00243 gnc_price_set_time(GNCPrice *p, Timespec t)
00244 {
00245 if(!p) return;
00246 if(!timespec_equal(&(p->tmspec), &t))
00247 {
00248
00249
00250
00251 gnc_price_ref (p);
00252 remove_price (p->db, p, FALSE);
00253 gnc_price_begin_edit (p);
00254 p->tmspec = t;
00255 gnc_price_set_dirty(p);
00256 gnc_price_commit_edit (p);
00257 add_price (p->db, p);
00258 gnc_price_unref (p);
00259 }
00260 }
00261
00262 void
00263 gnc_price_set_source(GNCPrice *p, const char *s)
00264 {
00265 if(!p) return;
00266 if(safe_strcmp(p->source, s) != 0)
00267 {
00268 char *tmp;
00269
00270 gnc_price_begin_edit (p);
00271 tmp = CACHE_INSERT((gpointer) s);
00272 if(p->source) CACHE_REMOVE(p->source);
00273 p->source = tmp;
00274 gnc_price_set_dirty(p);
00275 gnc_price_commit_edit (p);
00276 }
00277 }
00278
00279 void
00280 gnc_price_set_typestr(GNCPrice *p, const char* type)
00281 {
00282 if(!p) return;
00283 if(safe_strcmp(p->type, type) != 0)
00284 {
00285 gchar *tmp;
00286
00287 gnc_price_begin_edit (p);
00288 tmp = CACHE_INSERT((gpointer) type);
00289 if(p->type) CACHE_REMOVE(p->type);
00290 p->type = tmp;
00291 gnc_price_set_dirty(p);
00292 gnc_price_commit_edit (p);
00293 }
00294 }
00295
00296 void
00297 gnc_price_set_value(GNCPrice *p, gnc_numeric value)
00298 {
00299 if(!p) return;
00300 if(!gnc_numeric_eq(p->value, value))
00301 {
00302 gnc_price_begin_edit (p);
00303 p->value = value;
00304 gnc_price_set_dirty(p);
00305 gnc_price_commit_edit (p);
00306 }
00307 }
00308
00309
00310
00311
00312 GNCPrice *
00313 gnc_price_lookup (const GUID *guid, QofBook *book)
00314 {
00315 QofCollection *col;
00316
00317 if (!guid || !book) return NULL;
00318 col = qof_book_get_collection (book, GNC_ID_PRICE);
00319 return (GNCPrice *) qof_collection_lookup_entity (col, guid);
00320 }
00321
00322 gnc_commodity *
00323 gnc_price_get_commodity(const GNCPrice *p)
00324 {
00325 if(!p) return NULL;
00326 return p->commodity;
00327 }
00328
00329 Timespec
00330 gnc_price_get_time(const GNCPrice *p)
00331 {
00332 if(!p) {
00333 Timespec result;
00334 result.tv_sec = 0;
00335 result.tv_nsec = 0;
00336 return result;
00337 }
00338 return p->tmspec;
00339 }
00340
00341 const char *
00342 gnc_price_get_source(const GNCPrice *p)
00343 {
00344 if(!p) return NULL;
00345 return p->source;
00346 }
00347
00348 const char *
00349 gnc_price_get_typestr(const GNCPrice *p)
00350 {
00351 if(!p) return NULL;
00352 return p->type;
00353 }
00354
00355 gnc_numeric
00356 gnc_price_get_value(const GNCPrice *p)
00357 {
00358 if(!p) {
00359 PERR("price NULL.\n");
00360 return gnc_numeric_zero();
00361 }
00362 return p->value;
00363 }
00364
00365 gnc_commodity *
00366 gnc_price_get_currency(const GNCPrice *p)
00367 {
00368 if(!p) return NULL;
00369 return p->currency;
00370 }
00371
00372 gboolean
00373 gnc_price_equal (const GNCPrice *p1, const GNCPrice *p2)
00374 {
00375 Timespec ts1;
00376 Timespec ts2;
00377
00378 if (p1 == p2) return TRUE;
00379 if (!p1 || !p2) return FALSE;
00380
00381 if (!gnc_commodity_equiv (gnc_price_get_commodity (p1),
00382 gnc_price_get_commodity (p2)))
00383 return FALSE;
00384
00385 if (!gnc_commodity_equiv (gnc_price_get_currency (p1),
00386 gnc_price_get_currency (p2)))
00387 return FALSE;
00388
00389 ts1 = gnc_price_get_time (p1);
00390 ts2 = gnc_price_get_time (p2);
00391
00392 if (!timespec_equal (&ts1, &ts2))
00393 return FALSE;
00394
00395 if (safe_strcmp (gnc_price_get_source (p1),
00396 gnc_price_get_source (p2)) != 0)
00397 return FALSE;
00398
00399 if (safe_strcmp (gnc_price_get_typestr (p1),
00400 gnc_price_get_typestr (p2)) != 0)
00401 return FALSE;
00402
00403 if (!gnc_numeric_eq (gnc_price_get_value (p1),
00404 gnc_price_get_value (p2)))
00405 return FALSE;
00406
00407 return TRUE;
00408 }
00409
00410
00411
00412
00413 static gint
00414 compare_prices_by_date(gconstpointer a, gconstpointer b)
00415 {
00416 Timespec time_a;
00417 Timespec time_b;
00418 gint result;
00419
00420 if(!a && !b) return 0;
00421
00422 if(!a) return -1;
00423
00424 time_a = gnc_price_get_time((GNCPrice *) a);
00425 time_b = gnc_price_get_time((GNCPrice *) b);
00426
00427 result = -timespec_cmp(&time_a, &time_b);
00428 if (result) return result;
00429
00430
00431 return guid_compare (gnc_price_get_guid((GNCPrice *) a),
00432 gnc_price_get_guid((GNCPrice *) b));
00433 }
00434
00435 typedef struct {
00436 GNCPrice* pPrice;
00437 gboolean isDupl;
00438 } PriceListIsDuplStruct;
00439
00440 static void
00441 price_list_is_duplicate( gpointer data, gpointer user_data )
00442 {
00443 GNCPrice* pPrice = (GNCPrice*)data;
00444 PriceListIsDuplStruct* pStruct = (PriceListIsDuplStruct*)user_data;
00445 Timespec time_a, time_b;
00446
00447 time_a = timespecCanonicalDayTime( gnc_price_get_time( pPrice ) );
00448 time_b = timespecCanonicalDayTime( gnc_price_get_time( pStruct->pPrice ) );
00449
00450
00451 if( !gnc_numeric_equal( gnc_price_get_value( pPrice ), gnc_price_get_value( pStruct->pPrice ) ) ) return;
00452 if( gnc_price_get_commodity( pPrice ) != gnc_price_get_commodity( pStruct->pPrice ) ) return;
00453 if( gnc_price_get_currency( pPrice ) != gnc_price_get_currency( pStruct->pPrice ) ) return;
00454
00455 if( timespec_cmp( &time_a, &time_b ) != 0 ) return;
00456
00457 pStruct->isDupl = TRUE;
00458 }
00459
00460 gboolean
00461 gnc_price_list_insert(PriceList **prices, GNCPrice *p, gboolean check_dupl)
00462 {
00463 GList *result_list;
00464 PriceListIsDuplStruct* pStruct;
00465 gboolean isDupl;
00466
00467 if(!prices || !p) return FALSE;
00468 gnc_price_ref(p);
00469
00470 if (check_dupl) {
00471 pStruct = g_new0( PriceListIsDuplStruct, 1 );
00472 pStruct->pPrice = p;
00473 pStruct->isDupl = FALSE;
00474 g_list_foreach( *prices, price_list_is_duplicate, pStruct );
00475 isDupl = pStruct->isDupl;
00476 g_free( pStruct );
00477
00478 if( isDupl ) {
00479 return TRUE;
00480 }
00481 }
00482
00483 result_list = g_list_insert_sorted(*prices, p, compare_prices_by_date);
00484 if(!result_list) return FALSE;
00485 *prices = result_list;
00486 return TRUE;
00487 }
00488
00489 gboolean
00490 gnc_price_list_remove(PriceList **prices, GNCPrice *p)
00491 {
00492 GList *result_list;
00493 GList *found_element;
00494
00495 if(!prices || !p) return FALSE;
00496
00497 found_element = g_list_find(*prices, p);
00498 if(!found_element) return TRUE;
00499
00500 result_list = g_list_remove_link(*prices, found_element);
00501 gnc_price_unref((GNCPrice *) found_element->data);
00502 g_list_free(found_element);
00503
00504 *prices = result_list;
00505 return TRUE;
00506 }
00507
00508 static void
00509 price_list_destroy_helper(gpointer data, gpointer user_data)
00510 {
00511 gnc_price_unref((GNCPrice *) data);
00512 }
00513
00514 void
00515 gnc_price_list_destroy(PriceList *prices)
00516 {
00517 g_list_foreach(prices, price_list_destroy_helper, NULL);
00518 g_list_free(prices);
00519 }
00520
00521 gboolean
00522 gnc_price_list_equal(PriceList *prices1, PriceList *prices2)
00523 {
00524 GList *n1, *n2;
00525
00526 if (prices1 == prices2) return TRUE;
00527
00528 if (g_list_length (prices1) < g_list_length (prices2))
00529 {
00530 PWARN ("prices2 has extra prices");
00531 return FALSE;
00532 }
00533
00534 if (g_list_length (prices1) > g_list_length (prices2))
00535 {
00536 PWARN ("prices1 has extra prices");
00537 return FALSE;
00538 }
00539
00540 for (n1 = prices1, n2 = prices2; n1 ; n1 = n1->next, n2 = n2->next)
00541 if (!gnc_price_equal (n1->data, n2->data))
00542 return FALSE;
00543
00544 return TRUE;
00545 }
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559 QOF_GOBJECT_IMPL(gnc_pricedb, GNCPriceDB, QOF_TYPE_INSTANCE);
00560
00561 static void
00562 gnc_pricedb_init(GNCPriceDB* pdb)
00563 {
00564 }
00565
00566 static void
00567 gnc_pricedb_dispose_real (GObject *pdbp)
00568 {
00569 }
00570
00571 static void
00572 gnc_pricedb_finalize_real(GObject* pdbp)
00573 {
00574 }
00575
00576 static GNCPriceDB *
00577 gnc_pricedb_create(QofBook * book)
00578 {
00579 GNCPriceDB * result;
00580 QofCollection *col;
00581
00582 g_return_val_if_fail (book, NULL);
00583
00584
00585
00586
00587 col = qof_book_get_collection (book, GNC_ID_PRICEDB);
00588 result = qof_collection_get_data (col);
00589 if (result)
00590 {
00591 PWARN ("A price database already exists for this book!");
00592 return result;
00593 }
00594
00595 result = g_object_new(GNC_TYPE_PRICEDB, NULL);
00596 qof_instance_init_data (&result->inst, GNC_ID_PRICEDB, book);
00597 qof_collection_mark_clean(col);
00598
00602 qof_collection_set_data (col, result);
00603
00604 result->commodity_hash = g_hash_table_new(NULL, NULL);
00605 g_return_val_if_fail (result->commodity_hash, NULL);
00606 return result;
00607 }
00608
00609 static void
00610 destroy_pricedb_currency_hash_data(gpointer key,
00611 gpointer data,
00612 gpointer user_data)
00613 {
00614 GList *price_list = (GList *) data;
00615 GList *node;
00616 GNCPrice *p;
00617
00618 for (node = price_list; node; node = node->next)
00619 {
00620 p = node->data;
00621
00622 p->db = NULL;
00623 }
00624
00625 gnc_price_list_destroy(price_list);
00626 }
00627
00628 static void
00629 destroy_pricedb_commodity_hash_data(gpointer key,
00630 gpointer data,
00631 gpointer user_data)
00632 {
00633 GHashTable *currency_hash = (GHashTable *) data;
00634 if (!currency_hash) return;
00635 g_hash_table_foreach (currency_hash,
00636 destroy_pricedb_currency_hash_data,
00637 NULL);
00638 g_hash_table_destroy(currency_hash);
00639 }
00640
00641 void
00642 gnc_pricedb_destroy(GNCPriceDB *db)
00643 {
00644 if(!db) return;
00645 if(db->commodity_hash) {
00646 g_hash_table_foreach (db->commodity_hash,
00647 destroy_pricedb_commodity_hash_data,
00648 NULL);
00649 }
00650 g_hash_table_destroy (db->commodity_hash);
00651 db->commodity_hash = NULL;
00652
00653 g_object_unref(db);
00654 }
00655
00656 void
00657 gnc_pricedb_set_bulk_update(GNCPriceDB *db, gboolean bulk_update)
00658 {
00659 db->bulk_update = bulk_update;
00660 }
00661
00662
00663
00664
00665
00666
00667
00676 GNCPriceDB *
00677 gnc_collection_get_pricedb(QofCollection *col)
00678 {
00679 if (!col) return NULL;
00680 return qof_collection_get_data (col);
00681 }
00682
00683 GNCPriceDB *
00684 gnc_pricedb_get_db(QofBook *book)
00685 {
00686 QofCollection *col;
00687
00688 if (!book) return NULL;
00689 col = qof_book_get_collection (book, GNC_ID_PRICEDB);
00690 return gnc_collection_get_pricedb (col);
00691 }
00692
00693
00694
00695 static gboolean
00696 num_prices_helper (GNCPrice *p, gpointer user_data)
00697 {
00698 guint *count = user_data;
00699
00700 *count += 1;
00701
00702 return TRUE;
00703 }
00704
00705 guint
00706 gnc_pricedb_get_num_prices(GNCPriceDB *db)
00707 {
00708 guint count;
00709
00710 if (!db) return 0;
00711
00712 count = 0;
00713
00714 gnc_pricedb_foreach_price(db, num_prices_helper, &count, FALSE);
00715
00716 return count;
00717 }
00718
00719
00720
00721 typedef struct
00722 {
00723 gboolean equal;
00724 GNCPriceDB *db2;
00725 gnc_commodity *commodity;
00726 } GNCPriceDBEqualData;
00727
00728 static void
00729 pricedb_equal_foreach_pricelist(gpointer key, gpointer val, gpointer user_data)
00730 {
00731 GNCPriceDBEqualData *equal_data = user_data;
00732 gnc_commodity *currency = key;
00733 GList *price_list1 = val;
00734 GList *price_list2;
00735
00736 price_list2 = gnc_pricedb_get_prices (equal_data->db2,
00737 equal_data->commodity,
00738 currency);
00739
00740 if (!gnc_price_list_equal (price_list1, price_list2))
00741 equal_data->equal = FALSE;
00742
00743 gnc_price_list_destroy (price_list2);
00744 }
00745
00746 static void
00747 pricedb_equal_foreach_currencies_hash (gpointer key, gpointer val,
00748 gpointer user_data)
00749 {
00750 GHashTable *currencies_hash = val;
00751 GNCPriceDBEqualData *equal_data = user_data;
00752
00753 equal_data->commodity = key;
00754
00755 g_hash_table_foreach (currencies_hash,
00756 pricedb_equal_foreach_pricelist,
00757 equal_data);
00758 }
00759
00760 gboolean
00761 gnc_pricedb_equal (GNCPriceDB *db1, GNCPriceDB *db2)
00762 {
00763 GNCPriceDBEqualData equal_data;
00764
00765 if (db1 == db2) return TRUE;
00766
00767 if (!db1 || !db2)
00768 {
00769 PWARN ("one is NULL");
00770 return FALSE;
00771 }
00772
00773 equal_data.equal = TRUE;
00774 equal_data.db2 = db2;
00775
00776 g_hash_table_foreach (db1->commodity_hash,
00777 pricedb_equal_foreach_currencies_hash,
00778 &equal_data);
00779
00780 return equal_data.equal;
00781 }
00782
00783
00784
00785
00786
00787 static gboolean
00788 add_price(GNCPriceDB *db, GNCPrice *p)
00789 {
00790
00791
00792 GList *price_list;
00793 gnc_commodity *commodity;
00794 gnc_commodity *currency;
00795 GHashTable *currency_hash;
00796
00797 if(!db || !p) return FALSE;
00798 ENTER ("db=%p, pr=%p dirty=%d destroying=%d",
00799 db, p, qof_instance_get_dirty_flag(p),
00800 qof_instance_get_destroying(p));
00801
00802 if (!qof_instance_books_equal(db, p))
00803 {
00804 PERR ("attempted to mix up prices across different books");
00805 LEAVE (" ");
00806 return FALSE;
00807 }
00808
00809 commodity = gnc_price_get_commodity(p);
00810 if(!commodity) {
00811 PWARN("no commodity");
00812 LEAVE (" ");
00813 return FALSE;
00814 }
00815 currency = gnc_price_get_currency(p);
00816 if(!currency) {
00817 PWARN("no currency");
00818 LEAVE (" ");
00819 return FALSE;
00820 }
00821 if(!db->commodity_hash) { LEAVE ("no commodity hash found "); return FALSE; }
00822
00823 currency_hash = g_hash_table_lookup(db->commodity_hash, commodity);
00824 if(!currency_hash) {
00825 currency_hash = g_hash_table_new(NULL, NULL);
00826 g_hash_table_insert(db->commodity_hash, commodity, currency_hash);
00827 }
00828
00829 price_list = g_hash_table_lookup(currency_hash, currency);
00830 if(!gnc_price_list_insert(&price_list, p, !db->bulk_update))
00831 {
00832 LEAVE ("gnc_price_list_insert failed");
00833 return FALSE;
00834 }
00835 if(!price_list)
00836 {
00837 LEAVE (" no price list");
00838 return FALSE;
00839 }
00840 g_hash_table_insert(currency_hash, currency, price_list);
00841 p->db = db;
00842 qof_event_gen (&p->inst, QOF_EVENT_ADD, NULL);
00843
00844 LEAVE ("db=%p, pr=%p dirty=%d dextroying=%d commodity=%s/%s currency_hash=%p",
00845 db, p, qof_instance_get_dirty_flag(p),
00846 qof_instance_get_destroying(p),
00847 gnc_commodity_get_namespace(p->commodity),
00848 gnc_commodity_get_mnemonic(p->commodity),
00849 currency_hash);
00850 return TRUE;
00851 }
00852
00853
00854
00855 gboolean
00856 gnc_pricedb_add_price(GNCPriceDB *db, GNCPrice *p)
00857 {
00858 if(!db || !p) return FALSE;
00859
00860 ENTER ("db=%p, pr=%p dirty=%d destroying=%d",
00861 db, p, qof_instance_get_dirty_flag(p),
00862 qof_instance_get_destroying(p));
00863
00864 if (FALSE == add_price(db, p))
00865 {
00866 LEAVE (" failed to add price");
00867 return FALSE;
00868 }
00869
00870 gnc_pricedb_begin_edit(db);
00871 qof_instance_set_dirty(&db->inst);
00872 gnc_pricedb_commit_edit(db);
00873
00874 LEAVE ("db=%p, pr=%p dirty=%d destroying=%d",
00875 db, p, qof_instance_get_dirty_flag(p),
00876 qof_instance_get_destroying(p));
00877
00878 return TRUE;
00879 }
00880
00881
00882
00883
00884
00885 static gboolean
00886 remove_price(GNCPriceDB *db, GNCPrice *p, gboolean cleanup)
00887 {
00888 GList *price_list;
00889 gnc_commodity *commodity;
00890 gnc_commodity *currency;
00891 GHashTable *currency_hash;
00892
00893 if(!db || !p) return FALSE;
00894 ENTER ("db=%p, pr=%p dirty=%d destroying=%d",
00895 db, p, qof_instance_get_dirty_flag(p),
00896 qof_instance_get_destroying(p));
00897
00898 commodity = gnc_price_get_commodity(p);
00899 if(!commodity) { LEAVE (" no commodity"); return FALSE; }
00900 currency = gnc_price_get_currency(p);
00901 if(!currency) { LEAVE (" no currency"); return FALSE;}
00902 if(!db->commodity_hash)
00903 {
00904 LEAVE (" no commodity hash");
00905 return FALSE;
00906 }
00907
00908 currency_hash = g_hash_table_lookup(db->commodity_hash, commodity);
00909 if(!currency_hash) { LEAVE (" no currency hash"); return FALSE; }
00910
00911 qof_event_gen (&p->inst, QOF_EVENT_REMOVE, NULL);
00912 price_list = g_hash_table_lookup(currency_hash, currency);
00913 gnc_price_ref(p);
00914 if(!gnc_price_list_remove(&price_list, p)) {
00915 gnc_price_unref(p);
00916 LEAVE (" cannot remove price list");
00917 return FALSE;
00918 }
00919
00920
00921
00922 if(price_list) {
00923 g_hash_table_insert(currency_hash, currency, price_list);
00924 } else {
00925 g_hash_table_remove(currency_hash, currency);
00926
00927 if (cleanup) {
00928
00929
00930
00931 guint num_currencies = g_hash_table_size (currency_hash);
00932 if (0 == num_currencies) {
00933 g_hash_table_remove (db->commodity_hash, commodity);
00934 g_hash_table_destroy (currency_hash);
00935 }
00936 }
00937 }
00938
00939 gnc_price_unref(p);
00940 LEAVE ("db=%p, pr=%p", db, p);
00941 return TRUE;
00942 }
00943
00944 gboolean
00945 gnc_pricedb_remove_price(GNCPriceDB *db, GNCPrice *p)
00946 {
00947 gboolean rc;
00948 if(!db || !p) return FALSE;
00949 ENTER ("db=%p, pr=%p dirty=%d destroying=%d",
00950 db, p, qof_instance_get_dirty_flag(p),
00951 qof_instance_get_destroying(p));
00952
00953 gnc_price_ref(p);
00954 rc = remove_price (db, p, TRUE);
00955 gnc_pricedb_begin_edit(db);
00956 qof_instance_set_dirty(&db->inst);
00957 gnc_pricedb_commit_edit(db);
00958
00959
00960 gnc_price_begin_edit (p);
00961 qof_instance_set_destroying(p, TRUE);
00962 gnc_price_commit_edit (p);
00963 p->db = NULL;
00964 gnc_price_unref(p);
00965 LEAVE ("db=%p, pr=%p", db, p);
00966 return rc;
00967 }
00968
00969 typedef struct {
00970 GNCPriceDB *db;
00971 Timespec cutoff;
00972 gboolean delete_user;
00973 gboolean delete_last;
00974 GSList *list;
00975 } remove_info;
00976
00977 static gboolean
00978 check_one_price_date (GNCPrice *price, gpointer user_data)
00979 {
00980 remove_info *data = user_data;
00981 const gchar *source;
00982 Timespec pt;
00983
00984 ENTER("price %p (%s), data %p", price,
00985 gnc_commodity_get_mnemonic(gnc_price_get_commodity(price)),
00986 user_data);
00987 if (!data->delete_user) {
00988 source = gnc_price_get_source (price);
00989 if (safe_strcmp(source, "Finance::Quote") != 0) {
00990 LEAVE("Not an automatic quote");
00991 return TRUE;
00992 }
00993 }
00994
00995 pt = gnc_price_get_time (price);
00996 {
00997 gchar buf[40];
00998 gnc_timespec_to_iso8601_buff(pt , buf);
00999 DEBUG("checking date %s", buf);
01000 }
01001 if (timespec_cmp (&pt, &data->cutoff) < 0) {
01002 data->list = g_slist_prepend(data->list, price);
01003 DEBUG("will delete");
01004 }
01005 LEAVE(" ");
01006 return TRUE;
01007 }
01008
01009 static void
01010 pricedb_remove_foreach_pricelist (gpointer key,
01011 gpointer val,
01012 gpointer user_data)
01013 {
01014 GList *price_list = (GList *) val;
01015 GList *node = price_list;
01016 remove_info *data = (remove_info *) user_data;
01017
01018 ENTER("key %p, value %p, data %p", key, val, user_data);
01019
01020
01021 if (!data->delete_last)
01022 node = g_list_next(node);
01023
01024
01025 g_list_foreach(node, (GFunc)check_one_price_date, data);
01026
01027 LEAVE(" ");
01028 }
01029
01030 static void
01031 pricedb_remove_foreach_currencies_hash (gpointer key,
01032 gpointer val,
01033 gpointer user_data)
01034 {
01035 GHashTable *currencies_hash = (GHashTable *) val;
01036
01037 ENTER("key %p, value %p, data %p", key, val, user_data);
01038 g_hash_table_foreach(currencies_hash,
01039 pricedb_remove_foreach_pricelist, user_data);
01040 LEAVE(" ");
01041 }
01042
01043
01044 gboolean
01045 gnc_pricedb_remove_old_prices(GNCPriceDB *db,
01046 Timespec cutoff,
01047 gboolean delete_user,
01048 gboolean delete_last)
01049 {
01050 remove_info data;
01051 GSList *item;
01052
01053 data.db = db;
01054 data.cutoff = cutoff;
01055 data.delete_user = delete_user;
01056 data.delete_last = delete_last;
01057 data.list = NULL;
01058
01059 ENTER("db %p, delet_user %d, delete_last %d", db, delete_user, delete_last);
01060 {
01061 gchar buf[40];
01062 gnc_timespec_to_iso8601_buff(cutoff, buf);
01063 DEBUG("checking date %s", buf);
01064 }
01065
01066
01067
01068 g_hash_table_foreach(db->commodity_hash,
01069 pricedb_remove_foreach_currencies_hash,
01070 &data);
01071
01072 if (data.list == NULL)
01073 return FALSE;
01074
01075
01076 for (item = data.list; item; item = g_slist_next(item)) {
01077 gnc_pricedb_remove_price(db, item->data);
01078 }
01079
01080 g_slist_free(data.list);
01081 LEAVE(" ");
01082 return TRUE;
01083 }
01084
01085
01086
01087
01088 GNCPrice *
01089 gnc_pricedb_lookup_latest(GNCPriceDB *db,
01090 const gnc_commodity *commodity,
01091 const gnc_commodity *currency)
01092 {
01093 GList *price_list;
01094 GNCPrice *result;
01095 GHashTable *currency_hash;
01096 QofBook *book;
01097 QofBackend *be;
01098
01099 if(!db || !commodity || !currency) return NULL;
01100 ENTER ("db=%p commodity=%p currency=%p", db, commodity, currency);
01101 book = qof_instance_get_book(&db->inst);
01102 be = qof_book_get_backend(book);
01103 #ifdef GNUCASH_MAJOR_VERSION
01104 if (be && be->price_lookup)
01105 {
01106 GNCPriceLookup pl;
01107 pl.type = LOOKUP_LATEST;
01108 pl.prdb = db;
01109 pl.commodity = commodity;
01110 pl.currency = currency;
01111 (be->price_lookup) (be, &pl);
01112 }
01113 #endif
01114
01115 currency_hash = g_hash_table_lookup(db->commodity_hash, commodity);
01116 if(!currency_hash) { LEAVE (" no currency hash"); return NULL; }
01117
01118 price_list = g_hash_table_lookup(currency_hash, currency);
01119 if(!price_list) { LEAVE (" no price list"); return NULL; }
01120
01121
01122
01123
01124 result = price_list->data;
01125 gnc_price_ref(result);
01126 LEAVE(" ");
01127 return result;
01128 }
01129
01130
01131 static void
01132 lookup_latest(gpointer key, gpointer val, gpointer user_data)
01133 {
01134
01135 GList *price_list = (GList *)val;
01136 GList **return_list = (GList **)user_data;
01137
01138 if(!price_list) return;
01139
01140
01141 gnc_price_list_insert(return_list, price_list->data, FALSE);
01142 }
01143
01144 PriceList *
01145 gnc_pricedb_lookup_latest_any_currency(GNCPriceDB *db,
01146 const gnc_commodity *commodity)
01147 {
01148 GList *result;
01149 GHashTable *currency_hash;
01150 QofBook *book;
01151 QofBackend *be;
01152
01153 result = NULL;
01154
01155 if(!db || !commodity) return NULL;
01156 ENTER ("db=%p commodity=%p", db, commodity);
01157 book = qof_instance_get_book(&db->inst);
01158 be = qof_book_get_backend(book);
01159 #ifdef GNUCASH_MAJOR_VERSION
01160 if (be && be->price_lookup)
01161 {
01162 GNCPriceLookup pl;
01163 pl.type = LOOKUP_LATEST;
01164 pl.prdb = db;
01165 pl.commodity = commodity;
01166 pl.currency = NULL;
01167 (be->price_lookup) (be, &pl);
01168 }
01169 #endif
01170 currency_hash = g_hash_table_lookup(db->commodity_hash, commodity);
01171 if(!currency_hash) { LEAVE (" no currency hash"); return NULL; }
01172
01173 g_hash_table_foreach(currency_hash, lookup_latest, &result);
01174
01175 if(!result) { LEAVE (" "); return NULL; }
01176
01177 result = g_list_sort(result, compare_prices_by_date);
01178
01179 LEAVE(" ");
01180 return result;
01181 }
01182
01183
01184 static void
01185 hash_values_helper(gpointer key, gpointer value, gpointer data)
01186 {
01187 GList ** l = data;
01188 *l = g_list_concat(*l, g_list_copy (value));
01189 }
01190
01191 gboolean
01192 gnc_pricedb_has_prices(GNCPriceDB *db,
01193 const gnc_commodity *commodity,
01194 const gnc_commodity *currency)
01195 {
01196 GList *price_list;
01197 GHashTable *currency_hash;
01198 gint size;
01199 QofBook *book;
01200 QofBackend *be;
01201
01202 if(!db || !commodity) return FALSE;
01203 ENTER ("db=%p commodity=%p currency=%p", db, commodity, currency);
01204 book = qof_instance_get_book(&db->inst);
01205 be = qof_book_get_backend(book);
01206 #ifdef GNUCASH_MAJOR_VERSION
01207 if (book && be && be->price_lookup)
01208 {
01209 GNCPriceLookup pl;
01210 pl.type = LOOKUP_ALL;
01211 pl.prdb = db;
01212 pl.commodity = commodity;
01213 pl.currency = currency;
01214 (be->price_lookup) (be, &pl);
01215 }
01216 #endif
01217 currency_hash = g_hash_table_lookup(db->commodity_hash, commodity);
01218 if(!currency_hash) {
01219 LEAVE("no, no currency_hash table");
01220 return FALSE;
01221 }
01222
01223 if (currency) {
01224 price_list = g_hash_table_lookup(currency_hash, currency);
01225 if (price_list) {
01226 LEAVE("yes");
01227 return TRUE;
01228 }
01229 LEAVE("no, no price list");
01230 return FALSE;
01231 }
01232
01233 size = g_hash_table_size (currency_hash);
01234 LEAVE("%s", size > 0 ? "yes" : "no");
01235 return size > 0;
01236 }
01237
01238
01239 PriceList *
01240 gnc_pricedb_get_prices(GNCPriceDB *db,
01241 const gnc_commodity *commodity,
01242 const