diff options
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | Makefile.in | 8 | ||||
-rwxr-xr-x | configure | 98 | ||||
-rw-r--r-- | configure.in | 23 | ||||
-rw-r--r-- | src/amount.cc | 256 | ||||
-rw-r--r-- | src/amount.h | 152 | ||||
-rw-r--r-- | src/balance.cc | 126 | ||||
-rw-r--r-- | src/balance.h | 40 | ||||
-rw-r--r-- | src/commodity.cc | 543 | ||||
-rw-r--r-- | src/commodity.h | 393 | ||||
-rw-r--r-- | src/gnucash.cc | 4 | ||||
-rw-r--r-- | src/journal.cc | 19 | ||||
-rw-r--r-- | src/main.cc | 24 | ||||
-rw-r--r-- | src/qif.cc | 2 | ||||
-rw-r--r-- | src/quotes.cc | 69 | ||||
-rw-r--r-- | src/quotes.h | 12 | ||||
-rw-r--r-- | src/session.cc | 49 | ||||
-rw-r--r-- | src/session.h | 14 | ||||
-rw-r--r-- | src/system.hh | 9 | ||||
-rw-r--r-- | src/textual.cc | 16 | ||||
-rw-r--r-- | src/utils.h | 5 | ||||
-rw-r--r-- | src/value.cc | 126 | ||||
-rw-r--r-- | src/value.h | 9 | ||||
-rw-r--r-- | src/xmlparse.cc | 2 | ||||
-rw-r--r-- | src/xpath.cc | 4 | ||||
-rw-r--r-- | tests/numerics/BasicAmount.cc | 8 | ||||
-rw-r--r-- | tests/numerics/BasicAmount.h | 2 | ||||
-rw-r--r-- | tests/numerics/Commodity.cc | 13 | ||||
-rw-r--r-- | tests/numerics/Commodity.h | 2 | ||||
-rw-r--r-- | tests/numerics/CommodityAmount.cc | 5 | ||||
-rw-r--r-- | tests/numerics/CommodityAmount.h | 2 |
31 files changed, 1134 insertions, 903 deletions
diff --git a/Makefile.am b/Makefile.am index 6b98298c..434d1278 100644 --- a/Makefile.am +++ b/Makefile.am @@ -172,7 +172,7 @@ ledger_so_SOURCES = src/pyledger.cc ledger_so_DEPENDENCIES = libledger.la gdtoa/libgdtoa.la libpyledger.la PYLIBS = pyledger ledger gdtoa gmp boost_date_time \ - boost_filesystem boost_regex boost_python + boost_signals boost_filesystem boost_regex boost_python if HAVE_EXPAT PYLIBS += expat diff --git a/Makefile.in b/Makefile.in index 57abbd79..709b77e0 100644 --- a/Makefile.in +++ b/Makefile.in @@ -427,10 +427,10 @@ dist_lisp_LISP = lisp/ledger.el lisp/timeclock.el @HAVE_BOOST_PYTHON_TRUE@ledger_so_SOURCES = src/pyledger.cc @HAVE_BOOST_PYTHON_TRUE@ledger_so_DEPENDENCIES = libledger.la gdtoa/libgdtoa.la libpyledger.la @HAVE_BOOST_PYTHON_TRUE@PYLIBS = pyledger ledger gdtoa gmp \ -@HAVE_BOOST_PYTHON_TRUE@ boost_date_time boost_filesystem \ -@HAVE_BOOST_PYTHON_TRUE@ boost_regex boost_python \ -@HAVE_BOOST_PYTHON_TRUE@ $(am__append_15) $(am__append_16) \ -@HAVE_BOOST_PYTHON_TRUE@ $(am__append_17) +@HAVE_BOOST_PYTHON_TRUE@ boost_date_time boost_signals \ +@HAVE_BOOST_PYTHON_TRUE@ boost_filesystem boost_regex \ +@HAVE_BOOST_PYTHON_TRUE@ boost_python $(am__append_15) \ +@HAVE_BOOST_PYTHON_TRUE@ $(am__append_16) $(am__append_17) nodist_UnitTests_SOURCES = tests/UnitTests.cc \ \ tests/numerics/BasicAmount.cc \ @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.61 for ledger 3.0-git-lexical_cast. +# Generated by GNU Autoconf 2.61 for ledger 3.0-git-commodity_pool. # # Report bugs to <johnw@newartisans.com>. # @@ -728,8 +728,8 @@ SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='ledger' PACKAGE_TARNAME='ledger' -PACKAGE_VERSION='3.0-git-lexical_cast' -PACKAGE_STRING='ledger 3.0-git-lexical_cast' +PACKAGE_VERSION='3.0-git-commodity_pool' +PACKAGE_STRING='ledger 3.0-git-commodity_pool' PACKAGE_BUGREPORT='johnw@newartisans.com' ac_unique_file="ledger" @@ -1424,7 +1424,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures ledger 3.0-git-lexical_cast to adapt to many kinds of systems. +\`configure' configures ledger 3.0-git-commodity_pool to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1494,7 +1494,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of ledger 3.0-git-lexical_cast:";; + short | recursive ) echo "Configuration of ledger 3.0-git-commodity_pool:";; esac cat <<\_ACEOF @@ -1605,7 +1605,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -ledger configure 3.0-git-lexical_cast +ledger configure 3.0-git-commodity_pool generated by GNU Autoconf 2.61 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, @@ -1619,7 +1619,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by ledger $as_me 3.0-git-lexical_cast, which was +It was created by ledger $as_me 3.0-git-commodity_pool, which was generated by GNU Autoconf 2.61. Invocation command line was $ $0 $@ @@ -2310,7 +2310,7 @@ fi # Define the identity of the package. PACKAGE='ledger' - VERSION='3.0-git-lexical_cast' + VERSION='3.0-git-commodity_pool' cat >>confdefs.h <<_ACEOF @@ -19691,6 +19691,84 @@ See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi +# check for boost_signals +{ echo "$as_me:$LINENO: checking if boost_signals is available" >&5 +echo $ECHO_N "checking if boost_signals is available... $ECHO_C" >&6; } +if test "${boost_signals_cpplib_avail+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + boost_signals_save_libs=$LIBS + LIBS="-lboost_signals $LIBS" + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <boost/signal.hpp> +int +main () +{ +boost::signal<void (void)> this_signal; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + boost_signals_cpplib_avail=true +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + boost_signals_cpplib_avail=false +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + LIBS=$boost_signals_save_libs +fi +{ echo "$as_me:$LINENO: result: $boost_signals_cpplib_avail" >&5 +echo "${ECHO_T}$boost_signals_cpplib_avail" >&6; } + +if test x$boost_signals_cpplib_avail = xtrue ; then + LIBS="-lboost_signals $LIBS" +else + { { echo "$as_me:$LINENO: error: \"Could not find boost_signals library (set CPPFLAGS and LDFLAGS?)\" +See \`config.log' for more details." >&5 +echo "$as_me: error: \"Could not find boost_signals library (set CPPFLAGS and LDFLAGS?)\" +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + # check for gmp { echo "$as_me:$LINENO: checking if libgmp is available" >&5 echo $ECHO_N "checking if libgmp is available... $ECHO_C" >&6; } @@ -21967,7 +22045,7 @@ exec 6>&1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by ledger $as_me 3.0-git-lexical_cast, which was +This file was extended by ledger $as_me 3.0-git-commodity_pool, which was generated by GNU Autoconf 2.61. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -22020,7 +22098,7 @@ Report bugs to <bug-autoconf@gnu.org>." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_cs_version="\\ -ledger config.status 3.0-git-lexical_cast +ledger config.status 3.0-git-commodity_pool configured by $0, generated by GNU Autoconf 2.61, with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" diff --git a/configure.in b/configure.in index 769f3e8a..8af5e870 100644 --- a/configure.in +++ b/configure.in @@ -3,7 +3,7 @@ AC_PREREQ(2.59) -AC_INIT(ledger, 3.0-git-lexical_cast, johnw@newartisans.com) +AC_INIT(ledger, 3.0-git-commodity_pool, johnw@newartisans.com) AC_CONFIG_SRCDIR(ledger) AM_INIT_AUTOMAKE @@ -148,6 +148,27 @@ else AC_MSG_FAILURE("Could not find boost_filesystem library (set CPPFLAGS and LDFLAGS?)") fi +# check for boost_signals +AC_CACHE_CHECK( + [if boost_signals is available], + [boost_signals_cpplib_avail], + [boost_signals_save_libs=$LIBS + LIBS="-lboost_signals $LIBS" + AC_LANG_PUSH(C++) + AC_TRY_LINK( + [#include <boost/signal.hpp>], + [boost::signal<void (void)> this_signal;], + [boost_signals_cpplib_avail=true], + [boost_signals_cpplib_avail=false]) + AC_LANG_POP + LIBS=$boost_signals_save_libs]) + +if [test x$boost_signals_cpplib_avail = xtrue ]; then + LIBS="-lboost_signals $LIBS" +else + AC_MSG_FAILURE("Could not find boost_signals library (set CPPFLAGS and LDFLAGS?)") +fi + # check for gmp AC_CACHE_CHECK( [if libgmp is available], diff --git a/src/amount.cc b/src/amount.cc index b1ef1c03..6b9cfdd3 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -46,6 +46,8 @@ namespace ledger { +commodity_pool_t * amount_t::default_pool = NULL; + bool amount_t::keep_base = false; bool amount_t::keep_price = false; @@ -109,25 +111,12 @@ void amount_t::initialize() mpz_init(temp); mpz_init(divisor); + // jww (2007-05-02): Be very careful here! + if (! default_pool) + default_pool = new commodity_pool_t; + true_value = new amount_t::bigint_t; mpz_set_ui(true_value->val, 1); - - commodity_base_t::updater = NULL; - - commodity_t::commodities_by_ident = new commodities_array; - - commodity_t::default_commodity = NULL; - commodity_t::null_commodity = commodity_t::create(""); - commodity_t::null_commodity->add_flags(COMMODITY_STYLE_NOMARKET | - COMMODITY_STYLE_BUILTIN); - - // Add time commodity conversions, so that timelog's may be parsed - // in terms of seconds, but reported as minutes or hours. - commodity_t * commodity = commodity_t::create("s"); - commodity->add_flags(COMMODITY_STYLE_NOMARKET | COMMODITY_STYLE_BUILTIN); - - parse_conversion("1.0m", "60s"); - parse_conversion("1.0h", "60m"); } void amount_t::shutdown() @@ -135,30 +124,12 @@ void amount_t::shutdown() mpz_clear(temp); mpz_clear(divisor); - if (commodity_base_t::updater) { - checked_delete(commodity_base_t::updater); - commodity_base_t::updater = NULL; + // jww (2007-05-02): Be very careful here! + if (default_pool) { + checked_delete(default_pool); + default_pool = NULL; } - for (base_commodities_map::iterator i = commodity_base_t::commodities.begin(); - i != commodity_base_t::commodities.end(); - i++) - checked_delete((*i).second); - - for (commodities_map::iterator i = commodity_t::commodities.begin(); - i != commodity_t::commodities.end(); - i++) - checked_delete((*i).second); - - commodity_base_t::commodities.clear(); - commodity_t::commodities.clear(); - - checked_delete(commodity_t::commodities_by_ident); - commodity_t::commodities_by_ident = NULL; - - commodity_t::null_commodity = NULL; - commodity_t::default_commodity = NULL; - true_value->ref--; assert(true_value->ref == 0); checked_delete(true_value); @@ -408,9 +379,9 @@ amount_t& amount_t::operator+=(const amount_t& amt) if (commodity() != amt.commodity()) throw_(amount_error, "Adding amounts with different commodities: " << - (has_commodity() ? commodity_->qualified_symbol : "NONE") << + (has_commodity() ? commodity().symbol() : "NONE") << " != " << - (amt.has_commodity() ? amt.commodity_->qualified_symbol : "NONE")); + (amt.has_commodity() ? amt.commodity().symbol() : "NONE")); if (! amt.quantity) return *this; @@ -443,9 +414,9 @@ amount_t& amount_t::operator-=(const amount_t& amt) if (commodity() != amt.commodity()) throw_(amount_error, "Subtracting amounts with different commodities: " << - (has_commodity() ? commodity_->qualified_symbol : "NONE") << + (has_commodity() ? commodity().symbol() : "NONE") << " != " << - (amt.has_commodity() ? amt.commodity_->qualified_symbol : "NONE")); + (amt.has_commodity() ? amt.commodity().symbol() : "NONE")); if (! amt.quantity) return *this; @@ -529,9 +500,9 @@ amount_t& amount_t::operator*=(const amount_t& amt) commodity() != amt.commodity()) throw_(amount_error, "Multiplying amounts with different commodities: " << - (has_commodity() ? commodity_->qualified_symbol : "NONE") << + (has_commodity() ? commodity().symbol() : "NONE") << " != " << - (amt.has_commodity() ? amt.commodity_->qualified_symbol : "NONE")); + (amt.has_commodity() ? amt.commodity().symbol() : "NONE")); if (! amt.quantity) { *this = *this - *this; // preserve our commodity @@ -569,9 +540,9 @@ amount_t& amount_t::operator/=(const amount_t& amt) commodity() != amt.commodity()) throw_(amount_error, "Dividing amounts with different commodities: " << - (has_commodity() ? commodity_->qualified_symbol : "NONE") << + (has_commodity() ? commodity().symbol() : "NONE") << " != " << - (amt.has_commodity() ? amt.commodity_->qualified_symbol : "NONE")); + (amt.has_commodity() ? amt.commodity().symbol() : "NONE")); if (! amt.quantity || ! amt) { throw_(amount_error, "Divide by zero"); @@ -686,14 +657,14 @@ amount_t& amount_t::in_place_unreduce() return *this; } -amount_t amount_t::value(const moment_t& moment) const +optional<amount_t> amount_t::value(const optional<moment_t>& moment) const { if (quantity) { - amount_t amt(commodity().value(moment)); - if (! amt.realzero()) - return (amt * number()).round(); + optional<amount_t> amt(commodity().value(moment)); + if (amt) + return (*amt * number()).round(); } - return *this; + return optional<amount_t>(); } @@ -756,34 +727,29 @@ long amount_t::to_long() const } -void amount_t::annotate_commodity(const optional<amount_t>& tprice, - const optional<moment_t>& tdate, - const optional<string>& ttag) +void amount_t::annotate_commodity(const annotation_t& details) { - const commodity_t * this_base; + commodity_t * this_base; annotated_commodity_t * this_ann = NULL; if (commodity().annotated) { - this_ann = &static_cast<annotated_commodity_t&>(commodity()); - this_base = this_ann->ptr; + this_ann = &commodity().as_annotated(); + this_base = &this_ann->referent(); } else { this_base = &commodity(); } assert(this_base); DEBUG("amounts.commodities", "Annotating commodity for amount " - << *this << std::endl - << " price " << (tprice ? tprice->to_string() : "NONE") << " " - << " date " << (tdate ? *tdate : moment_t()) << " " - << " ttag " << (ttag ? *ttag : "NONE")); + << *this << std::endl << details); if (commodity_t * ann_comm = - annotated_commodity_t::find_or_create - (*this_base, - ! tprice && this_ann ? this_ann->price : tprice, - ! tdate && this_ann ? this_ann->date : tdate, - ! ttag && this_ann ? this_ann->tag : ttag)) + this_base->parent().find_or_create(*this_base, details)) set_commodity(*ann_comm); +#ifdef ASSERTS_ON + else + assert(false); +#endif DEBUG("amounts.commodities", " Annotated amount is " << *this); } @@ -802,69 +768,48 @@ amount_t amount_t::strip_annotations(const bool _keep_price, << " keep date " << _keep_date << " " << " keep tag " << _keep_tag); - annotated_commodity_t& - ann_comm(static_cast<annotated_commodity_t&>(commodity())); - assert(ann_comm.base); - - commodity_t * new_comm; + annotated_commodity_t& ann_comm(commodity().as_annotated()); + commodity_t * new_comm; - if ((_keep_price && ann_comm.price) || - (_keep_date && ann_comm.date) || - (_keep_tag && ann_comm.tag)) + if ((_keep_price && ann_comm.details.price) || + (_keep_date && ann_comm.details.date) || + (_keep_tag && ann_comm.details.tag)) { - new_comm = annotated_commodity_t::find_or_create - (*ann_comm.ptr, - _keep_price ? ann_comm.price : optional<amount_t>(), - _keep_date ? ann_comm.date : optional<moment_t>(), - _keep_tag ? ann_comm.tag : optional<string>()); + new_comm = ann_comm.parent().find_or_create + (ann_comm.referent(), + annotation_t(_keep_price ? ann_comm.details.price : optional<amount_t>(), + _keep_date ? ann_comm.details.date : optional<moment_t>(), + _keep_tag ? ann_comm.details.tag : optional<string>())); } else { - new_comm = commodity_t::find_or_create(ann_comm.base_symbol()); + new_comm = ann_comm.parent().find_or_create(ann_comm.base_symbol()); } assert(new_comm); amount_t t(*this); t.set_commodity(*new_comm); - DEBUG("amounts.commodities", " Reduced amount is " << t); + + DEBUG("amounts.commodities", " Stripped amount is " << t); return t; } -optional<amount_t> amount_t::price() const +bool amount_t::commodity_annotated() const { - if (commodity_ && commodity_->annotated && - ((annotated_commodity_t *)commodity_)->price) { - amount_t t(*((annotated_commodity_t *)commodity_)->price); - t *= number(); - DEBUG("amounts.commodities", - "Returning price of " << *this << " = " << t); - return t; - } - return optional<amount_t>(); + assert(! commodity().annotated || commodity().as_annotated().details); + return commodity().annotated; } -optional<moment_t> amount_t::date() const +annotation_t amount_t::annotation_details() const { - if (commodity_ && commodity_->annotated) { - DEBUG("amounts.commodities", - "Returning date of " << *this << " = " - << ((annotated_commodity_t *)commodity_)->date); - return ((annotated_commodity_t *)commodity_)->date; - } - return optional<moment_t>(); -} + assert(! commodity().annotated || commodity().as_annotated().details); -optional<string> amount_t::tag() const -{ - if (commodity_ && commodity_->annotated) { - DEBUG("amounts.commodities", - "Returning tag of " << *this << " = " - << ((annotated_commodity_t *)commodity_)->tag); - return ((annotated_commodity_t *)commodity_)->tag; + if (commodity().annotated) { + annotated_commodity_t& ann_comm(commodity().as_annotated()); + return ann_comm.details; } - return optional<string>(); + return annotation_t(); } - static void parse_quantity(std::istream& in, string& value) { char buf[256]; @@ -923,16 +868,15 @@ static void parse_commodity(std::istream& in, string& symbol) symbol = buf; } -void parse_annotations(std::istream& in, - optional<amount_t>& price, - optional<moment_t>& date, - optional<string>& tag) +bool parse_annotations(commodity_pool_t& parent, + std::istream& in, + annotation_t& details) { do { char buf[256]; char c = peek_next_nonws(in); if (c == '{') { - if (price) + if (details.price) throw_(amount_error, "Commodity specifies more than one price"); in.get(c); @@ -943,7 +887,7 @@ void parse_annotations(std::istream& in, throw_(amount_error, "Commodity price lacks closing brace"); amount_t temp; - temp.parse(buf, AMOUNT_PARSE_NO_MIGRATE); + temp.parse(parent, buf, AMOUNT_PARSE_NO_MIGRATE); temp.in_place_reduce(); // Since this price will maintain its own precision, make sure @@ -954,10 +898,10 @@ void parse_annotations(std::istream& in, temp.quantity->prec < temp.commodity().precision()) temp = temp.round(); // no need to retain individual precision - price = temp; + details.price = temp; } else if (c == '[') { - if (date) + if (details.date) throw_(amount_error, "Commodity specifies more than one date"); in.get(c); @@ -967,10 +911,10 @@ void parse_annotations(std::istream& in, else throw_(amount_error, "Commodity date lacks closing bracket"); - date = parse_datetime(buf); + details.date = parse_datetime(buf); } else if (c == '(') { - if (tag) + if (details.tag) throw_(amount_error, "Commodity specifies more than one tag"); in.get(c); @@ -980,7 +924,7 @@ void parse_annotations(std::istream& in, else throw_(amount_error, "Commodity tag lacks closing parenthesis"); - tag = buf; + details.tag = buf; } else { break; @@ -989,25 +933,28 @@ void parse_annotations(std::istream& in, DEBUG("amounts.commodities", "Parsed commodity annotations: " - << " price " << (price ? price->to_string() : "NONE") << " " - << " date " << (date ? *date : moment_t()) << " " - << " tag " << (tag ? *tag : "NONE")); + << " price " + << (details.price ? details.price->to_string() : "NONE") << " " + << " date " + << (details.date ? *details.date : moment_t()) << " " + << " tag " + << (details.tag ? *details.tag : "NONE")); + + return details; } -void amount_t::parse(std::istream& in, uint8_t flags) +void amount_t::parse(commodity_pool_t& parent, std::istream& in, uint8_t flags) { // The possible syntax for an amount is: // // [-]NUM[ ]SYM [@ AMOUNT] // SYM[ ][-]NUM [@ AMOUNT] - string symbol; - string quant; - optional<amount_t> tprice; - optional<moment_t> tdate; - optional<string> ttag; - unsigned int comm_flags = COMMODITY_STYLE_DEFAULTS; - bool negative = false; + string symbol; + string quant; + annotation_t details; + unsigned int comm_flags = COMMODITY_STYLE_DEFAULTS; + bool negative = false; char c = peek_next_nonws(in); if (c == '-') { @@ -1030,7 +977,7 @@ void amount_t::parse(std::istream& in, uint8_t flags) comm_flags |= COMMODITY_STYLE_SUFFIXED; if (! in.eof() && ((n = in.peek()) != '\n')) - parse_annotations(in, tprice, tdate, ttag); + parse_annotations(parent, in, details); } } else { parse_commodity(in, symbol); @@ -1042,7 +989,7 @@ void amount_t::parse(std::istream& in, uint8_t flags) parse_quantity(in, quant); if (! quant.empty() && ! in.eof() && ((n = in.peek()) != '\n')) - parse_annotations(in, tprice, tdate, ttag); + parse_annotations(parent, in, details); } } @@ -1059,16 +1006,15 @@ void amount_t::parse(std::istream& in, uint8_t flags) if (symbol.empty()) { commodity_ = NULL; } else { - commodity_ = commodity_t::find(symbol); + commodity_ = parent.find(symbol); if (! commodity_) { - commodity_ = commodity_t::create(symbol); + commodity_ = parent.create(symbol); newly_created = true; } assert(commodity_); - if (tprice || tdate || ttag) - commodity_ = annotated_commodity_t::find_or_create - (*commodity_, tprice, tdate, ttag); + if (details) + commodity_ = parent.find_or_create(*commodity_, details); } // Determine the precision of the amount, based on the usage of @@ -1100,7 +1046,8 @@ void amount_t::parse(std::istream& in, uint8_t flags) // Set the commodity's flags and precision accordingly - if (commodity_ && (newly_created || ! (flags & AMOUNT_PARSE_NO_MIGRATE))) { + if (commodity_ && + (newly_created || ! (flags & AMOUNT_PARSE_NO_MIGRATE))) { commodity().add_flags(comm_flags); if (quantity->prec > commodity().precision()) commodity().set_precision(quantity->prec); @@ -1138,13 +1085,14 @@ void amount_t::parse(std::istream& in, uint8_t flags) in_place_reduce(); } -void amount_t::parse_conversion(const string& larger_str, +void amount_t::parse_conversion(commodity_pool_t& parent, + const string& larger_str, const string& smaller_str) { amount_t larger, smaller; - larger.parse(larger_str, AMOUNT_PARSE_NO_REDUCE); - smaller.parse(smaller_str, AMOUNT_PARSE_NO_REDUCE); + larger.parse(parent, larger_str, AMOUNT_PARSE_NO_REDUCE); + smaller.parse(parent, smaller_str, AMOUNT_PARSE_NO_REDUCE); larger *= smaller.number(); @@ -1323,7 +1271,7 @@ void amount_t::print(std::ostream& _out, bool omit_commodity, if (! omit_commodity && comm.annotated) { annotated_commodity_t& ann(static_cast<annotated_commodity_t&>(comm)); - assert(&*ann.price != this); + assert(&*ann.details.price != this); ann.write_annotations(out); } @@ -1337,30 +1285,34 @@ void amount_t::print(std::ostream& _out, bool omit_commodity, } -void amount_t::read(std::istream& in) +void amount_t::read(commodity_pool_t& parent, std::istream& in) { commodity_t::ident_t ident; read_binary_long(in, ident); if (ident == 0xffffffff) commodity_ = NULL; else if (ident == 0) - commodity_ = commodity_t::null_commodity; - else - commodity_ = (*commodity_t::commodities_by_ident)[ident - 1]; + commodity_ = parent.null_commodity; + else { + commodity_ = parent.find(ident - 1); + assert(commodity_); + } read_quantity(in); } -void amount_t::read(char *& data) +void amount_t::read(commodity_pool_t& parent, char *& data) { commodity_t::ident_t ident; read_binary_long(data, ident); if (ident == 0xffffffff) commodity_ = NULL; else if (ident == 0) - commodity_ = commodity_t::null_commodity; - else - commodity_ = (*commodity_t::commodities_by_ident)[ident - 1]; + commodity_ = parent.null_commodity; + else { + commodity_ = parent.find(ident - 1); + assert(commodity_); + } read_quantity(data); } diff --git a/src/amount.h b/src/amount.h index 0c9501bc..280613d7 100644 --- a/src/amount.h +++ b/src/amount.h @@ -52,6 +52,8 @@ namespace ledger { class commodity_t; +class annotation_t; +class commodity_pool_t; DECLARE_EXCEPTION(amount_error); @@ -88,6 +90,12 @@ public: static void shutdown(); /** + * The default_pool is a static variable indicating which commodity + * pool should be used when none is specified. + */ + static commodity_pool_t * default_pool; + + /** * The `keep_base' member determines whether scalable commodities * are automatically converted to their most reduced form when * printing. The default is true. @@ -291,11 +299,12 @@ public: * compact form greater than 1.0. That is, 3599s will unreduce to * 59.98m, while 3601 unreduces to 1h. * - * value(moment_t) returns the history value of an amount, based on - * the price history of its commodity. For example, if the amount - * were 10 AAPL, and on Apr 10, 2000 each share of AAPL was worth - * $10, then call value() for that moment in time would yield the - * amount $100.00. + * value(optional<moment_t>) returns the historical value for an + * amount -- the default moment returns the most recently known + * price -- based on the price history of its commodity. For + * example, if the amount were 10 AAPL, and on Apr 10, 2000 each + * share of AAPL was worth $10, then call value() for that moment in + * time would yield the amount $100.00. * * Further, for the sake of efficiency and avoiding temporary * objects, the following methods support "in-place" variants that @@ -341,7 +350,8 @@ public: } amount_t& in_place_unreduce(); - amount_t value(const moment_t& moment) const; + optional<amount_t> value(const optional<moment_t>& moment = + optional<moment_t>()) const; /** * Truth tests. An amount may be truth test in several ways: @@ -466,38 +476,26 @@ public: * the price argument is required, although it can be passed as * `optional<amount_t>()' if no price is desired. * + * commodity_annotated() returns true if an amount's commodity has + * any annotation details associated with it. + * + * annotation_details() returns all of the details of an annotated + * commodity's annotations. The structure returns will evaluate as + * boolean false if there are no details. + * * strip_annotations([keep_price, keep_date, keep_tag]) returns an * amount whose commodity's annotations have been stripped. The * three `keep_' arguments determine which annotation detailed are * kept, meaning that the default is to follow whatever * amount_t::keep_price, amount_t::keep_date and amount_t::keep_tag * have been set to (which all default to false). - * - * price() returns an amount's annotated commodity's price. This - * return value is of type `optional<amount_t>', so it must be - * tested for boolean truth to determine if an annotated price even - * existed. - * - * date() returns an amount's annotated commodity's date. This - * return value is of type `optional<moment_t>'. - * - * tag() returns an amount's annotated commodity's tag. This return - * value is of type `optional<string>'. */ - void annotate_commodity(const optional<amount_t>& tprice, - const optional<moment_t>& tdate = optional<moment_t>(), - const optional<string>& ttag = optional<string>()); - - amount_t strip_annotations(const bool _keep_price = keep_price, - const bool _keep_date = keep_date, - const bool _keep_tag = keep_tag) const; - - optional<amount_t> price() const; - optional<moment_t> date() const; - optional<string> tag() const; - -#define AMOUNT_PARSE_NO_MIGRATE 0x01 -#define AMOUNT_PARSE_NO_REDUCE 0x02 + void annotate_commodity(const annotation_t& details); + bool commodity_annotated() const; + annotation_t annotation_details() const; + amount_t strip_annotations(const bool _keep_price = keep_price, + const bool _keep_date = keep_date, + const bool _keep_tag = keep_tag) const; /** * Parsing methods. The method `parse' is used to parse an amount @@ -505,11 +503,18 @@ public: * defined which simply calls parse on the input stream. The * `parse' method has two forms: * - * parse(istream, unsigned char flags) parses an amount from the - * given input stream. + * parse(commodity_pool_t, istream, flags_t) parses an + * amount from the given input stream, registering commodity details + * according to the commodity pool which is passed in as the first + * parameter. + * + * parse(istream, flags_t) is the same as the preceding function, + * only it uses `amount_t::default_pool' as the commodity pool. * - * parse(string, unsigned char flags) parses an amount from the - * given string. + * parse(commodity_pool_t, string, flags_t) parses an amount from + * the given string. + * + * parse(string, flags_t) also parses an amount from a string. * * The `flags' argument of both parsing may be one or more of the * following: @@ -538,15 +543,36 @@ public: * amount_t::parse_conversion("1.0m", "60s"); // a minute is 60 seconds * amount_t::parse_conversion("1.0h", "60m"); // an hour is 60 minutes */ - void parse(std::istream& in, unsigned char flags = 0); - void parse(const string& str, unsigned char flags = 0) { +#define AMOUNT_PARSE_NO_MIGRATE 0x01 +#define AMOUNT_PARSE_NO_REDUCE 0x02 + + typedef uint_least8_t flags_t; + + void parse(commodity_pool_t& parent, std::istream& in, flags_t flags = 0); + void parse(commodity_pool_t& parent, const string& str, flags_t flags = 0) { std::istringstream stream(str); - parse(stream, flags); + parse(parent, stream, flags); } - static void parse_conversion(const string& larger_str, + void parse(std::istream& in, flags_t flags = 0) { + assert(default_pool); + parse(*default_pool, in, flags); + } + void parse(const string& str, flags_t flags = 0) { + assert(default_pool); + parse(*default_pool, str, flags); + } + + static void parse_conversion(commodity_pool_t& parent, + const string& larger_str, const string& smaller_str); + static void parse_conversion(const string& larger_str, + const string& smaller_str) { + assert(default_pool); + parse_conversion(*default_pool, larger_str, smaller_str); + } + /** * Printing methods. An amount may be output to a stream using the * `print' method. There is also a global operator<< defined which @@ -571,18 +597,40 @@ public: * input stream or a character pointer, and it may be serialized to * an output stream. The methods used are: * - * read(istream) reads an amount from the given input stream. It - * must have been put there using `write(ostream)'. + * read(commodity_pool_t, istream) reads an amount from the given + * input stream. It must have been put there using + * `write(ostream)'. Also, the given pool must be exactly what it + * was at the time the amount was `written'. Thus, the required + * flow of logic is: + * amount.write(out) + * pool.write(out) + * pool.read(in) + * amount.read(pool, in) + * + * read(istream) does the same as read, only it relies on + * `amount_t::default_pool' to specify the pool. + * + * read(commodity_pool_t, char *&) reads an amount from data which + * has been read from an input stream into a buffer. it advances + * the pointer passed in to the end of the deserialized amount. * - * read(char *&) reads an amount from data which has been read from - * an input stream into a buffer. it advances the pointer passed in - * to the end of the deserialized amount. + * read(char *&) does the same as read, only it relies on + * `amount_t::default_pool' to specify the pool. * * write(ostream) writes an amount to an output stream in a compact * binary format. */ - void read(std::istream& in); - void read(char *& data); + void read(commodity_pool_t& parent, std::istream& in); + void read(commodity_pool_t& parent, char *& data); + + void read(std::istream& in) { + assert(default_pool); + read(*default_pool, in); + } + void read(char *& data) { + assert(default_pool); + read(*default_pool, data); + } void write(std::ostream& out) const; @@ -614,10 +662,9 @@ public: bool valid() const; private: - friend void parse_annotations(std::istream& in, - optional<amount_t>& price, - optional<moment_t>& date, - optional<string>& tag); + friend bool parse_annotations(commodity_pool_t& parent, + std::istream& in, + annotation_t& details); }; inline amount_t amount_t::exact(const string& value) { @@ -672,11 +719,12 @@ inline amount_t amount_t::round() const { } inline bool amount_t::has_commodity() const { - return commodity_ && commodity_ != commodity_t::null_commodity; + return commodity_ && commodity_ != commodity_->parent().null_commodity; } inline commodity_t& amount_t::commodity() const { - return has_commodity() ? *commodity_ : *commodity_t::null_commodity; + // jww (2007-05-02): Should be a way to access null_commodity better + return has_commodity() ? *commodity_ : *default_pool->null_commodity; } } // namespace ledger diff --git a/src/balance.cc b/src/balance.cc index 1baad2bf..dd822963 100644 --- a/src/balance.cc +++ b/src/balance.cc @@ -133,7 +133,8 @@ balance_t& balance_t::operator/=(const amount_t& amt) return *this; } -amount_t balance_t::amount(const commodity_t& commodity) const +optional<amount_t> +balance_t::amount(const optional<const commodity_t&>& commodity) const { if (! commodity) { if (amounts.size() == 1) { @@ -151,71 +152,27 @@ amount_t balance_t::amount(const commodity_t& commodity) const } } else if (amounts.size() > 0) { - amounts_map::const_iterator i = amounts.find(&commodity); + amounts_map::const_iterator i = amounts.find(&*commodity); if (i != amounts.end()) return (*i).second; } - return amount_t(); + return optional<amount_t>(); } -balance_t balance_t::value(const moment_t& moment) const -{ - balance_t temp; - - for (amounts_map::const_iterator i = amounts.begin(); - i != amounts.end(); - i++) - temp += (*i).second.value(moment); - - return temp; -} - -optional<balance_t> balance_t::price() const +optional<balance_t> +balance_t::value(const optional<moment_t>& moment) const { optional<balance_t> temp; for (amounts_map::const_iterator i = amounts.begin(); i != amounts.end(); - i++) { - optional<amount_t> i_price = (*i).second.price(); - if (i_price) { + i++) + if (optional<amount_t> val = (*i).second.value(moment)) { if (! temp) temp = balance_t(); - *temp += *i_price; + *temp += *val; } - } - return temp; -} -optional<moment_t> balance_t::date() const -{ - optional<moment_t> temp; - - for (amounts_map::const_iterator i = amounts.begin(); - i != amounts.end(); - i++) { - optional<moment_t> tdate = (*i).second.date(); - if (! temp && tdate) - temp = *tdate; - else if (temp && tdate && temp != tdate) - return optional<moment_t>(); - } - return temp; -} - -optional<string> balance_t::tag() const -{ - optional<string> temp; - - for (amounts_map::const_iterator i = amounts.begin(); - i != amounts.end(); - i++) { - optional<string> ttag = (*i).second.tag(); - if (! temp && ttag) - temp = *ttag; - else if (temp && ttag && temp != ttag) - return optional<string>(); - } return temp; } @@ -243,52 +200,33 @@ void balance_t::write(std::ostream& out, if (lwidth == -1) lwidth = first_width; - if (commodity_t::commodities_sorted) { - for (amounts_map::const_iterator i = amounts.begin(); - i != amounts.end(); - i++) { - int width; - if (! first) { - out << std::endl; - width = lwidth; - } else { - first = false; - width = first_width; - } + typedef std::vector<const amount_t *> amounts_array; + amounts_array sorted; - out.width(width); - out.fill(' '); - out << std::right << (*i).second; - } - } else { - typedef std::vector<const amount_t *> amounts_array; - amounts_array sorted; - - for (amounts_map::const_iterator i = amounts.begin(); - i != amounts.end(); - i++) - if ((*i).second) - sorted.push_back(&(*i).second); + for (amounts_map::const_iterator i = amounts.begin(); + i != amounts.end(); + i++) + if ((*i).second) + sorted.push_back(&(*i).second); - std::stable_sort(sorted.begin(), sorted.end(), - compare_amount_commodities()); + std::stable_sort(sorted.begin(), sorted.end(), + compare_amount_commodities()); - for (amounts_array::const_iterator i = sorted.begin(); - i != sorted.end(); - i++) { - int width; - if (! first) { - out << std::endl; - width = lwidth; - } else { - first = false; - width = first_width; - } - - out.width(width); - out.fill(' '); - out << std::right << **i; + for (amounts_array::const_iterator i = sorted.begin(); + i != sorted.end(); + i++) { + int width; + if (! first) { + out << std::endl; + width = lwidth; + } else { + first = false; + width = first_width; } + + out.width(width); + out.fill(' '); + out << std::right << **i; } if (first) { diff --git a/src/balance.h b/src/balance.h index 38693bda..62f9ba86 100644 --- a/src/balance.h +++ b/src/balance.h @@ -93,15 +93,19 @@ public: bool operator<(const balance_t& bal) const { for (amounts_map::const_iterator i = bal.amounts.begin(); i != bal.amounts.end(); - i++) - if (! (amount(*(*i).first) < (*i).second)) + i++) { + optional<amount_t> amt = amount(*(*i).first); + if (amt && ! (*amt < (*i).second)) return false; + } for (amounts_map::const_iterator i = amounts.begin(); i != amounts.end(); - i++) - if (! ((*i).second < bal.amount(*(*i).first))) + i++) { + optional<amount_t> amt = bal.amount(*(*i).first); + if (amt && ! ((*i).second < *amt)) return false; + } if (bal.amounts.size() == 0 && amounts.size() == 0) return false; @@ -146,13 +150,10 @@ public: return true; } - amount_t amount(const commodity_t& commodity = - *commodity_t::null_commodity) const; - balance_t value(const moment_t& moment = now) const; - - optional<balance_t> price() const; - optional<moment_t> date() const; - optional<string> tag() const; + optional<amount_t> amount(const optional<const commodity_t&>& commodity = + optional<const commodity_t&>()) const; + optional<balance_t> value(const optional<moment_t>& moment = + optional<moment_t>()) const; balance_t strip_annotations(const bool keep_price = amount_t::keep_price, @@ -335,24 +336,15 @@ public: return temp; } - amount_t amount(const commodity_t& commodity = - *commodity_t::null_commodity) const { + optional<amount_t> amount(const optional<const commodity_t&>& commodity = + optional<const commodity_t&>()) const { return quantity.amount(commodity); } - balance_t value(const moment_t& moment = now) const { + optional<balance_t> value(const optional<moment_t>& moment = + optional<moment_t>()) const { return quantity.value(moment); } - optional<balance_t> price() const { - return quantity.price(); - } - optional<moment_t> date() const { - return quantity.date(); - } - optional<string> tag() const { - return quantity.tag(); - } - balance_t strip_annotations(const bool keep_price = amount_t::keep_price, const bool keep_date = amount_t::keep_date, diff --git a/src/commodity.cc b/src/commodity.cc index dba1eb98..1fac4a4e 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -43,61 +43,98 @@ namespace ledger { -#ifndef THREADSAFE -base_commodities_map commodity_base_t::commodities; - -commodity_base_t::updater_t * commodity_base_t::updater = NULL; - -commodities_map commodity_t::commodities; -commodities_array * commodity_t::commodities_by_ident; -bool commodity_t::commodities_sorted = false; -commodity_t * commodity_t::null_commodity; -commodity_t * commodity_t::default_commodity = NULL; -#endif - -void commodity_base_t::add_price(const moment_t& date, - const amount_t& price) +void commodity_t::add_price(const moment_t& date, + const amount_t& price) { - if (! history) - history = history_t(); + if (! base->history) + base->history = history_t(); - history_map::iterator i = history->prices.find(date); - if (i != history->prices.end()) { + history_map::iterator i = base->history->prices.find(date); + if (i != base->history->prices.end()) { (*i).second = price; } else { std::pair<history_map::iterator, bool> result - = history->prices.insert(history_pair(date, price)); + = base->history->prices.insert(history_pair(date, price)); assert(result.second); } } -bool commodity_base_t::remove_price(const moment_t& date) +bool commodity_t::remove_price(const moment_t& date) { - if (history) { - history_map::size_type n = history->prices.erase(date); + if (base->history) { + history_map::size_type n = base->history->prices.erase(date); if (n > 0) { - if (history->prices.empty()) - history.reset(); + if (base->history->prices.empty()) + base->history.reset(); return true; } } return false; } -commodity_base_t * commodity_base_t::create(const string& symbol) +optional<amount_t> commodity_t::value(const optional<moment_t>& moment) { - commodity_base_t * commodity = new commodity_base_t(symbol); + optional<moment_t> age; + optional<amount_t> price; - DEBUG("amounts.commodities", "Creating base commodity " << symbol); + if (base->history) { + assert(base->history->prices.size() > 0); + + if (! moment) { + history_map::reverse_iterator r = base->history->prices.rbegin(); + age = (*r).first; + price = (*r).second; + } else { + history_map::iterator i = base->history->prices.lower_bound(*moment); + if (i == base->history->prices.end()) { + history_map::reverse_iterator r = base->history->prices.rbegin(); + age = (*r).first; + price = (*r).second; + } else { + age = (*i).first; + if (*moment != *age) { + if (i != base->history->prices.begin()) { + --i; + age = (*i).first; + price = (*i).second; + } else { + age = optional<moment_t>(); + } + } else { + price = (*i).second; + } + } + } + } + + if (! (flags() & COMMODITY_STYLE_NOMARKET)) { + if (optional<amount_t> quote = parent().get_quote + (*this, age, moment, + (base->history && base->history->prices.size() > 0 ? + (*base->history->prices.rbegin()).first : optional<moment_t>()))) + return *quote; + } + return price; +} + +commodity_t::operator bool() const +{ + return this != parent().null_commodity; +} - std::pair<base_commodities_map::iterator, bool> result - = commodities.insert(base_commodities_pair(symbol, commodity)); - assert(result.second); +annotated_commodity_t& commodity_t::as_annotated() +{ + assert(annotated); + return *polymorphic_downcast<annotated_commodity_t *>(this); +} - return commodity; +const annotated_commodity_t& commodity_t::as_annotated() const +{ + assert(annotated); + return *polymorphic_downcast<const annotated_commodity_t *>(this); } -bool commodity_t::needs_quotes(const string& symbol) +bool commodity_t::symbol_needs_quotes(const string& symbol) { for (const char * p = symbol.c_str(); *p; p++) if (std::isspace(*p) || std::isdigit(*p) || *p == '-' || *p == '.') @@ -108,7 +145,7 @@ bool commodity_t::needs_quotes(const string& symbol) bool commodity_t::valid() const { - if (symbol().empty() && this != null_commodity) { + if (symbol().empty() && this != parent().null_commodity) { DEBUG("ledger.validate", "commodity_t: symbol().empty() && this != null_commodity"); return false; @@ -127,204 +164,225 @@ bool commodity_t::valid() const return true; } -commodity_t * commodity_t::create(const string& symbol) +bool annotated_commodity_t::operator==(const commodity_t& comm) const { - std::auto_ptr<commodity_t> commodity(new commodity_t); - - commodity->base = commodity_base_t::create(symbol); - - if (needs_quotes(symbol)) { - commodity->qualified_symbol = "\""; - commodity->qualified_symbol += symbol; - commodity->qualified_symbol += "\""; - } else { - commodity->qualified_symbol = symbol; - } - - DEBUG("amounts.commodities", - "Creating commodity " << commodity->qualified_symbol); - - std::pair<commodities_map::iterator, bool> result - = commodities.insert(commodities_pair(symbol, commodity.get())); - if (! result.second) - return NULL; + // If the base commodities don't match, the game's up. + if (base != comm.base) + return false; - commodity->ident = commodities_by_ident->size(); - commodities_by_ident->push_back(commodity.get()); + assert(annotated); + if (! comm.annotated) + return false; - // Start out the new commodity with the default commodity's flags - // and precision, if one has been defined. - if (default_commodity) - commodity->drop_flags(COMMODITY_STYLE_THOUSANDS | - COMMODITY_STYLE_NOMARKET); + if (details != comm.as_annotated().details) + return false; - return commodity.release(); + return true; } -commodity_t * commodity_t::find_or_create(const string& symbol) +void +annotated_commodity_t::write_annotations(std::ostream& out, + const annotation_t& info) { - DEBUG("amounts.commodities", "Find-or-create commodity " << symbol); + if (info.price) + out << " {" << *info.price << '}'; - commodity_t * commodity = find(symbol); - if (commodity) - return commodity; - return create(symbol); + if (info.date) + out << " [" << *info.date << ']'; + + if (info.tag) + out << " (" << *info.tag << ')'; } -commodity_t * commodity_t::find(const string& symbol) +bool compare_amount_commodities::operator()(const amount_t * left, + const amount_t * right) const { - DEBUG("amounts.commodities", "Find commodity " << symbol); + commodity_t& leftcomm(left->commodity()); + commodity_t& rightcomm(right->commodity()); - commodities_map::const_iterator i = commodities.find(symbol); - if (i != commodities.end()) - return (*i).second; - return NULL; -} + int cmp = leftcomm.base_symbol().compare(rightcomm.base_symbol()); + if (cmp != 0) + return cmp < 0; -amount_t commodity_base_t::value(const moment_t& moment) -{ - moment_t age; - amount_t price; + if (! leftcomm.annotated) { + assert(rightcomm.annotated); + return true; + } + else if (! rightcomm.annotated) { + assert(leftcomm.annotated); + return false; + } + else { + annotated_commodity_t& aleftcomm(static_cast<annotated_commodity_t&>(leftcomm)); + annotated_commodity_t& arightcomm(static_cast<annotated_commodity_t&>(rightcomm)); - if (history) { - assert(history->prices.size() > 0); + if (! aleftcomm.details.price && arightcomm.details.price) + return true; + if (aleftcomm.details.price && ! arightcomm.details.price) + return false; - if (! is_valid_moment(moment)) { - history_map::reverse_iterator r = history->prices.rbegin(); - age = (*r).first; - price = (*r).second; - } else { - history_map::iterator i = history->prices.lower_bound(moment); - if (i == history->prices.end()) { - history_map::reverse_iterator r = history->prices.rbegin(); - age = (*r).first; - price = (*r).second; + if (aleftcomm.details.price && arightcomm.details.price) { + amount_t leftprice(*aleftcomm.details.price); + leftprice.in_place_reduce(); + amount_t rightprice(*arightcomm.details.price); + rightprice.in_place_reduce(); + + if (leftprice.commodity() == rightprice.commodity()) { + return (leftprice - rightprice).sign() < 0; } else { - age = (*i).first; - if (moment != age) { - if (i != history->prices.begin()) { - --i; - age = (*i).first; - price = (*i).second; - } else { - age = moment_t(); - } - } else { - price = (*i).second; - } + // Since we have two different amounts, there's really no way + // to establish a true sorting order; we'll just do it based + // on the numerical values. + leftprice.clear_commodity(); + rightprice.clear_commodity(); + return (leftprice - rightprice).sign() < 0; } } + + if (! aleftcomm.details.date && arightcomm.details.date) + return true; + if (aleftcomm.details.date && ! arightcomm.details.date) + return false; + + if (aleftcomm.details.date && arightcomm.details.date) { + duration_t diff = *aleftcomm.details.date - *arightcomm.details.date; + return diff.is_negative(); + } + + if (! aleftcomm.details.tag && arightcomm.details.tag) + return true; + if (aleftcomm.details.tag && ! arightcomm.details.tag) + return false; + + if (aleftcomm.details.tag && arightcomm.details.tag) + return *aleftcomm.details.tag < *arightcomm.details.tag; + + assert(false); + return true; } +} - if (updater && ! (flags & COMMODITY_STYLE_NOMARKET)) - (*updater)(*this, moment, age, - (history && history->prices.size() > 0 ? - (*history->prices.rbegin()).first : moment_t()), price); +commodity_pool_t::commodity_pool_t() : default_commodity(NULL) +{ + null_commodity = create(""); + null_commodity->add_flags(COMMODITY_STYLE_NOMARKET | + COMMODITY_STYLE_BUILTIN); - return price; + // Add time commodity conversions, so that timelog's may be parsed + // in terms of seconds, but reported as minutes or hours. + commodity_t * commodity = create("s"); + commodity->add_flags(COMMODITY_STYLE_NOMARKET | COMMODITY_STYLE_BUILTIN); + + amount_t::parse_conversion(*this, "1.0m", "60s"); + amount_t::parse_conversion(*this, "1.0h", "60m"); } -bool annotated_commodity_t::operator==(const commodity_t& comm) const +commodity_t * commodity_pool_t::create(const string& symbol) { - // If the base commodities don't match, the game's up. - if (base != comm.base) - return false; + shared_ptr<commodity_base_t> base_commodity(new commodity_base_t(symbol)); + std::auto_ptr<commodity_t> commodity(new commodity_t(this, base_commodity)); - if (price && - (! comm.annotated || - price != static_cast<const annotated_commodity_t&>(comm).price)) - return false; + DEBUG("amounts.commodities", "Creating base commodity " << symbol); - if (date && - (! comm.annotated || - date != static_cast<const annotated_commodity_t&>(comm).date)) - return false; + // Create the "qualified symbol" version of this commodity's symbol + if (commodity_t::symbol_needs_quotes(symbol)) { + commodity->qualified_symbol = "\""; + *commodity->qualified_symbol += symbol; + *commodity->qualified_symbol += "\""; + } - if (tag && - (! comm.annotated || - tag != static_cast<const annotated_commodity_t&>(comm).tag)) - return false; + DEBUG("amounts.commodities", + "Creating commodity '" << commodity->symbol() << "'"); - return true; + // Start out the new commodity with the default commodity's flags + // and precision, if one has been defined. +#if 0 + // jww (2007-05-02): This doesn't do anything currently! + if (default_commodity) + commodity->drop_flags(COMMODITY_STYLE_THOUSANDS | + COMMODITY_STYLE_NOMARKET); +#endif + + commodity->ident = commodities.size(); + + std::pair<commodities_t::iterator, bool> result = + commodities.insert(commodity.get()); + if (! result.second) { + assert(false); + return NULL; + } else { + return commodity.release(); + } } -void -annotated_commodity_t::write_annotations(std::ostream& out, - const optional<amount_t>& price, - const optional<moment_t>& date, - const optional<string>& tag) +commodity_t * commodity_pool_t::find_or_create(const string& symbol) { - if (price) - out << " {" << *price << '}'; - - if (date) - out << " [" << *date << ']'; + DEBUG("amounts.commodities", "Find-or-create commodity " << symbol); - if (tag) - out << " (" << *tag << ')'; + commodity_t * commodity = find(symbol); + if (commodity) + return commodity; + return create(symbol); } -commodity_t * -annotated_commodity_t::create(const commodity_t& comm, - const optional<amount_t>& price, - const optional<moment_t>& date, - const optional<string>& tag, - const string& mapping_key) +commodity_t * commodity_pool_t::find(const string& symbol) { - std::auto_ptr<annotated_commodity_t> commodity(new annotated_commodity_t); + DEBUG("amounts.commodities", "Find commodity " << symbol); - // Set the annotated bits - commodity->price = price; - commodity->date = date; - commodity->tag = tag; + typedef commodity_pool_t::commodities_t::nth_index<1>::type + commodities_by_name; - commodity->ptr = &comm; - assert(commodity->ptr); - commodity->base = comm.base; - assert(commodity->base); + commodities_by_name& name_index = commodities.get<1>(); + commodities_by_name::const_iterator i = name_index.find(symbol); + if (i != name_index.end()) + return *i; + else + return NULL; +} - commodity->qualified_symbol = comm.symbol(); +commodity_t * commodity_pool_t::find(const commodity_t::ident_t ident) +{ + DEBUG("amounts.commodities", "Find commodity by ident " << ident); - DEBUG("amounts.commodities", "Creating annotated commodity " - << "symbol " << commodity->symbol() - << " key " << mapping_key << std::endl - << " price " << (price ? price->to_string() : "NONE") << " " - << " date " << (date ? *date : moment_t()) << " " - << " tag " << (tag ? *tag : "NONE")); + typedef commodity_pool_t::commodities_t::nth_index<0>::type + commodities_by_ident; - // Add the fully annotated name to the map, so that this symbol may - // quickly be found again. - std::pair<commodities_map::iterator, bool> result - = commodities.insert(commodities_pair(mapping_key, commodity.get())); - if (! result.second) + commodities_by_ident& ident_index = commodities.get<0>(); + commodities_by_ident::iterator i = ident_index.find(ident); + if (i != ident_index.end()) + return *i; + else return NULL; +} - commodity->ident = commodities_by_ident->size(); - commodities_by_ident->push_back(commodity.get()); +commodity_t * +commodity_pool_t::create(const string& symbol, const annotation_t& details) +{ + commodity_t * new_comm = create(symbol); + if (! new_comm) + return NULL; - return commodity.release(); + if (details) + return find_or_create(*new_comm, details); + else + return new_comm; } namespace { - string make_qualified_name(const commodity_t& comm, - const optional<amount_t>& price, - const optional<moment_t>& date, - const optional<string>& tag) + string make_qualified_name(const commodity_t& comm, + const annotation_t& details) { - if (price && price->sign() < 0) + assert(details); + + if (details.price && details.price->sign() < 0) throw_(amount_error, "A commodity's price may not be negative"); std::ostringstream name; - comm.write(name); - annotated_commodity_t::write_annotations(name, price, date, tag); + annotated_commodity_t::write_annotations(name, details); DEBUG("amounts.commodities", "make_qualified_name for " - << comm.qualified_symbol << std::endl - << " price " << (price ? price->to_string() : "NONE") << " " - << " date " << (date ? *date : moment_t()) << " " - << " tag " << (tag ? *tag : "NONE")); - + << comm.qualified_symbol << std::endl << details); DEBUG("amounts.commodities", "qualified_name is " << name.str()); return name.str(); @@ -332,87 +390,88 @@ namespace { } commodity_t * -annotated_commodity_t::find_or_create(const commodity_t& comm, - const optional<amount_t>& price, - const optional<moment_t>& date, - const optional<string>& tag) +commodity_pool_t::find(const string& symbol, const annotation_t& details) { - string name = make_qualified_name(comm, price, date, tag); + commodity_t * comm = find(symbol); + if (! comm) + return NULL; - commodity_t * ann_comm = commodity_t::find(name); - if (ann_comm) { - assert(ann_comm->annotated); - return ann_comm; + if (details) { + string name = make_qualified_name(*comm, details); + + if (commodity_t * ann_comm = find(name)) { + assert(ann_comm->annotated && + ann_comm->as_annotated().details); + return ann_comm; + } + return NULL; + } else { + return comm; } - return create(comm, price, date, tag, name); } -bool compare_amount_commodities::operator()(const amount_t * left, - const amount_t * right) const +commodity_t * +commodity_pool_t::find_or_create(const string& symbol, + const annotation_t& details) { - commodity_t& leftcomm(left->commodity()); - commodity_t& rightcomm(right->commodity()); + commodity_t * comm = find(symbol); + if (! comm) + return NULL; - int cmp = leftcomm.base_symbol().compare(rightcomm.base_symbol()); - if (cmp != 0) - return cmp < 0; + if (details) + return find_or_create(*comm, details); + else + return comm; +} - if (! leftcomm.annotated) { - assert(rightcomm.annotated); - return true; - } - else if (! rightcomm.annotated) { - assert(leftcomm.annotated); - return false; - } - else { - annotated_commodity_t& aleftcomm(static_cast<annotated_commodity_t&>(leftcomm)); - annotated_commodity_t& arightcomm(static_cast<annotated_commodity_t&>(rightcomm)); +commodity_t * +commodity_pool_t::create(commodity_t& comm, + const annotation_t& details, + const string& mapping_key) +{ + assert(comm); + assert(details); + assert(! mapping_key.empty()); - if (! aleftcomm.price && arightcomm.price) - return true; - if (aleftcomm.price && ! arightcomm.price) - return false; + std::auto_ptr<commodity_t> commodity + (new annotated_commodity_t(&comm, details)); - if (aleftcomm.price && arightcomm.price) { - amount_t leftprice(*aleftcomm.price); - leftprice.in_place_reduce(); - amount_t rightprice(*arightcomm.price); - rightprice.in_place_reduce(); + commodity->qualified_symbol = comm.symbol(); + assert(! commodity->qualified_symbol->empty()); - if (leftprice.commodity() == rightprice.commodity()) { - return (leftprice - rightprice).sign() < 0; - } else { - // Since we have two different amounts, there's really no way - // to establish a true sorting order; we'll just do it based - // on the numerical values. - leftprice.clear_commodity(); - rightprice.clear_commodity(); - return (leftprice - rightprice).sign() < 0; - } - } + DEBUG("amounts.commodities", "Creating annotated commodity " + << "symbol " << commodity->symbol() + << " key " << mapping_key << std::endl << details); - if (! aleftcomm.date && arightcomm.date) - return true; - if (aleftcomm.date && ! arightcomm.date) - return false; + // Add the fully annotated name to the map, so that this symbol may + // quickly be found again. + commodity->ident = commodities.size(); + commodity->mapping_key_ = mapping_key; - if (aleftcomm.date && arightcomm.date) { - duration_t diff = *aleftcomm.date - *arightcomm.date; - return diff.is_negative(); - } + std::pair<commodities_t::iterator, bool> result + = commodities.insert(commodity.get()); + if (! result.second) { + assert(false); + return NULL; + } else { + return commodity.release(); + } +} - if (! aleftcomm.tag && arightcomm.tag) - return true; - if (aleftcomm.tag && ! arightcomm.tag) - return false; +commodity_t * commodity_pool_t::find_or_create(commodity_t& comm, + const annotation_t& details) +{ + assert(comm); + assert(details); - if (aleftcomm.tag && arightcomm.tag) - return *aleftcomm.tag < *arightcomm.tag; + string name = make_qualified_name(comm, details); + assert(! name.empty()); - assert(false); - return true; + if (commodity_t * ann_comm = find(name)) { + assert(ann_comm->annotated && ann_comm->as_annotated().details); + return ann_comm; } + return create(comm, details, name); } } // namespace ledger diff --git a/src/commodity.h b/src/commodity.h index def41778..29534729 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -53,232 +53,229 @@ namespace ledger { #define COMMODITY_STYLE_NOMARKET 0x0010 #define COMMODITY_STYLE_BUILTIN 0x0020 -typedef std::map<const moment_t, amount_t> history_map; -typedef std::pair<const moment_t, amount_t> history_pair; - -class commodity_base_t; - -typedef std::map<const string, commodity_base_t *> base_commodities_map; -typedef std::pair<const string, commodity_base_t *> base_commodities_pair; - class commodity_base_t : public noncopyable { -public: +private: + friend class commodity_pool_t; friend class commodity_t; friend class annotated_commodity_t; - friend void amount_t::initialize(); - friend void amount_t::shutdown(); + typedef std::map<const moment_t, amount_t> history_map; + typedef std::pair<const moment_t, amount_t> history_pair; - friend void checked_delete<commodity_base_t>(commodity_base_t *); + struct history_t { + history_map prices; + ptime last_lookup; + }; - typedef uint_least32_t ident_t; + typedef uint_least8_t flags_t; - ident_t ident; - string name; - string note; + flags_t flags; + string symbol; amount_t::precision_t precision; - unsigned char flags; + optional<string> name; + optional<string> note; + optional<history_t> history; optional<amount_t> smaller; optional<amount_t> larger; - commodity_base_t() : precision(0), flags(COMMODITY_STYLE_DEFAULTS) { +public: + explicit commodity_base_t() + : flags(COMMODITY_STYLE_DEFAULTS), precision(0) { TRACE_CTOR(commodity_base_t, ""); } - - commodity_base_t(const commodity_base_t&) { - TRACE_CTOR(commodity_base_t, "copy"); - assert(0); - } - - commodity_base_t(const string& _symbol, - unsigned int _precision = 0, - unsigned int _flags = COMMODITY_STYLE_DEFAULTS) - : precision(_precision), flags(_flags), symbol(_symbol) { - TRACE_CTOR(commodity_base_t, "const string&, unsigned int, unsigned int"); + explicit commodity_base_t + (const string& _symbol, + amount_t::precision_t _precision = 0, + unsigned int _flags = COMMODITY_STYLE_DEFAULTS) + : flags(_flags), symbol(_symbol), precision(_precision) { + TRACE_CTOR(commodity_base_t, + "const string&, amount_t::precision_t, unsigned int"); } - ~commodity_base_t() { TRACE_DTOR(commodity_base_t); } - - static base_commodities_map commodities; - - static commodity_base_t * create(const string& symbol); - - string symbol; - - struct history_t { - history_map prices; - ptime last_lookup; - history_t() : last_lookup() {} - }; - optional<history_t> history; - - void add_price(const moment_t& date, const amount_t& price); - bool remove_price(const moment_t& date); - amount_t value(const moment_t& moment = now); - -public: - class updater_t { - public: - virtual ~updater_t() {} - virtual void operator()(commodity_base_t& commodity, - const moment_t& moment, - const moment_t& date, - const moment_t& last, - amount_t& price) = 0; - }; - friend class updater_t; - - static updater_t * updater; }; -typedef std::map<const string, commodity_t *> commodities_map; -typedef std::pair<const string, commodity_t *> commodities_pair; - -typedef std::vector<commodity_t *> commodities_array; +class annotated_commodity_t; class commodity_t - : public equality_comparable<commodity_t, noncopyable> + : public equality_comparable1<commodity_t, noncopyable> { - friend class annotated_commodity_t; - public: - // This map remembers all commodities that have been defined. - - static commodities_map commodities; - static commodities_array * commodities_by_ident; - static bool commodities_sorted; - static commodity_t * null_commodity; - static commodity_t * default_commodity; - - static commodity_t * create(const string& symbol); - static commodity_t * find(const string& name); - static commodity_t * find_or_create(const string& symbol); + static bool symbol_needs_quotes(const string& symbol); - static bool needs_quotes(const string& symbol); + typedef commodity_base_t::flags_t flags_t; + typedef commodity_base_t::history_t history_t; + typedef commodity_base_t::history_map history_map; + typedef commodity_base_t::history_pair history_pair; + typedef uint_least32_t ident_t; - static void make_alias(const string& symbol, - commodity_t * commodity); - - // These are specific to each commodity reference - - typedef unsigned long ident_t; + shared_ptr<commodity_base_t> base; + commodity_pool_t * parent_; ident_t ident; - commodity_base_t * base; - string qualified_symbol; + optional<string> qualified_symbol; + optional<string> mapping_key_; bool annotated; public: - explicit commodity_t() : base(NULL), annotated(false) { + explicit commodity_t(commodity_pool_t * _parent, + const shared_ptr<commodity_base_t>& _base) + : base(_base), parent_(_parent), annotated(false) { TRACE_CTOR(commodity_t, ""); } - commodity_t(const commodity_t& o) - : ident(o.ident), base(o.base), - qualified_symbol(o.qualified_symbol), annotated(o.annotated) { - TRACE_CTOR(commodity_t, "copy"); - } virtual ~commodity_t() { TRACE_DTOR(commodity_t); } - operator bool() const { - return this != null_commodity; - } + operator bool() const; + virtual bool operator==(const commodity_t& comm) const { if (comm.annotated) return comm == *this; - return base == comm.base; + return base.get() == comm.base.get(); } + commodity_pool_t& parent() const { + return *parent_; + } + + annotated_commodity_t& as_annotated(); + const annotated_commodity_t& as_annotated() const; + string base_symbol() const { return base->symbol; } string symbol() const { - return qualified_symbol; + return qualified_symbol ? *qualified_symbol : base_symbol(); } - void write(std::ostream& out) const { - out << symbol(); + string mapping_key() const { + if (mapping_key_) + return *mapping_key_; + else + return base_symbol(); } - string name() const { + optional<string> name() const { return base->name; } - void set_name(const string& arg) { + void set_name(const optional<string>& arg = optional<string>()) { base->name = arg; } - string note() const { + optional<string> note() const { return base->note; } - void set_note(const string& arg) { + void set_note(const optional<string>& arg = optional<string>()) { base->note = arg; } - unsigned char precision() const { + amount_t::precision_t precision() const { return base->precision; } - void set_precision(unsigned char arg) { + void set_precision(amount_t::precision_t arg) { base->precision = arg; } - unsigned char flags() const { + flags_t flags() const { return base->flags; } - void set_flags(unsigned char arg) { + void set_flags(flags_t arg) { base->flags = arg; } - void add_flags(unsigned char arg) { + void add_flags(flags_t arg) { base->flags |= arg; } - void drop_flags(unsigned char arg) { + void drop_flags(flags_t arg) { base->flags &= ~arg; } optional<amount_t> smaller() const { return base->smaller; } - void set_smaller(const amount_t& arg) { + void set_smaller(const optional<amount_t>& arg = optional<amount_t>()) { base->smaller = arg; } optional<amount_t> larger() const { return base->larger; } - void set_larger(const amount_t& arg) { + void set_larger(const optional<amount_t>& arg = optional<amount_t>()) { base->larger = arg; } - optional<commodity_base_t::history_t> history() const { + optional<history_t> history() const { return base->history; } - void add_price(const moment_t& date, const amount_t& price) { - return base->add_price(date, price); - } - bool remove_price(const moment_t& date) { - return base->remove_price(date); - } - amount_t value(const moment_t& moment = now) const { - return base->value(moment); + void add_price(const moment_t& date, const amount_t& price); + bool remove_price(const moment_t& date); + + optional<amount_t> value(const optional<moment_t>& moment = + optional<moment_t>()); + + void write(std::ostream& out) const { + out << symbol(); } bool valid() const; }; -class annotated_commodity_t : public commodity_t +inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) { + comm.write(out); + return out; +} + +struct annotation_t : public equality_comparable<annotation_t> { - public: - const commodity_t * ptr; + optional<amount_t> price; + optional<moment_t> date; + optional<string> tag; - optional<amount_t> price; - optional<moment_t> date; - optional<string> tag; + explicit annotation_t(const optional<amount_t>& _price = optional<amount_t>(), + const optional<moment_t>& _date = optional<moment_t>(), + const optional<string>& _tag = optional<string>()) + : price(_price), date(_date), tag(_tag) {} - explicit annotated_commodity_t() { + operator bool() const { + return price || date || tag; + } + + bool operator==(const annotation_t& rhs) const { + return (price == rhs.price && + date == rhs.date && + tag == rhs.tag); + } + + void write(std::ostream& out) const { + out << "price " << (price ? price->to_string() : "NONE") << " " + << "date " << (date ? *date : moment_t()) << " " + << "tag " << (tag ? *tag : "NONE"); + } + + bool valid() const { + assert(*this); + } +}; + +inline std::ostream& operator<<(std::ostream& out, const annotation_t& details) { + details.write(out); + return out; +} + +class annotated_commodity_t + : public commodity_t, + equality_comparable1<annotated_commodity_t, noncopyable> +{ +public: + commodity_t * ptr; + annotation_t details; + + explicit annotated_commodity_t(commodity_t * _ptr, + const annotation_t& _details) + : commodity_t(_ptr->parent_, _ptr->base), ptr(_ptr), details(_details) { TRACE_CTOR(annotated_commodity_t, ""); annotated = true; } @@ -288,37 +285,129 @@ class annotated_commodity_t : public commodity_t virtual bool operator==(const commodity_t& comm) const; + commodity_t& referent() { + return *ptr; + } + const commodity_t& referent() const { + return *ptr; + } + void write_annotations(std::ostream& out) const { - annotated_commodity_t::write_annotations(out, price, date, tag); + annotated_commodity_t::write_annotations(out, details); + } + + static void write_annotations(std::ostream& out, + const annotation_t& info); +}; + +struct compare_amount_commodities { + bool operator()(const amount_t * left, const amount_t * right) const; +}; + +class commodity_pool_t : public noncopyable +{ +public: + /** + * The commodities collection in commodity_pool_t maintains pointers + * to all the commodities which have ever been created by the user, + * whether explicitly by calling the create methods of + * commodity_pool_t, or implicitly by parsing a commoditized amount. + * + * The `commodities' member variable represents a collection which + * is indexed by two vertices: first, and ordered sequence of unique + * integer which identify commodities by a numerical identifier; and + * second, by a hashed set of symbolic names which reflect how the + * commodity was referred to by the user. + */ + typedef multi_index_container< + commodity_t *, + multi_index::indexed_by< + multi_index::ordered_unique< + multi_index::member<commodity_t, + commodity_t::ident_t, &commodity_t::ident> >, + multi_index::hashed_unique< + multi_index::const_mem_fun<commodity_t, + string, &commodity_t::mapping_key> > + > + > commodities_t; + + commodities_t commodities; + commodity_t * null_commodity; + commodity_t * default_commodity; + +private: + template<typename T> + struct first_initialized + { + typedef T result_type; + + template<typename InputIterator> + T operator()(InputIterator first, InputIterator last) const + { + for (; first != last; first++) + if (*first) + return *first; + return T(); + } + }; + +public: + boost::signal<optional<amount_t> + (commodity_t& commodity, + const optional<moment_t>& date, + const optional<moment_t>& moment, + const optional<moment_t>& last), + first_initialized<optional<amount_t> > > get_quote; + + explicit commodity_pool_t(); + + ~commodity_pool_t() { + typedef commodity_pool_t::commodities_t::nth_index<0>::type + commodities_by_ident; + + commodities_by_ident& ident_index = commodities.get<0>(); + for (commodities_by_ident::iterator i = ident_index.begin(); + i != ident_index.end(); + i++) + checked_delete(*i); } - static void write_annotations(std::ostream& out, - const optional<amount_t>& price, - const optional<moment_t>& date, - const optional<string>& tag); + commodity_t * create(const string& symbol); + commodity_t * find(const string& name); + commodity_t * find(const commodity_t::ident_t ident); + commodity_t * find_or_create(const string& symbol); - private: - static commodity_t * create(const commodity_t& comm, - const optional<amount_t>& price, - const optional<moment_t>& date, - const optional<string>& tag, - const string& mapping_key); + commodity_t * create(const string& symbol, const annotation_t& details); + commodity_t * find(const string& symbol, const annotation_t& details); + commodity_t * find_or_create(const string& symbol, + const annotation_t& details); - static commodity_t * find_or_create(const commodity_t& comm, - const optional<amount_t>& price, - const optional<moment_t>& date, - const optional<string>& tag); + commodity_t * create(commodity_t& comm, + const annotation_t& details, + const string& mapping_key); - friend class amount_t; -}; + commodity_t * find_or_create(commodity_t& comm, + const annotation_t& details); -inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) { - out << comm.symbol(); - return out; -} + void parse_amount(amount_t& amount, std::istream& in, + amount_t::flags_t flags = 0) { + amount.parse(*this, in, flags); + } + void parse_amount(amount_t& amount, const string& str, + amount_t::flags_t flags = 0) { + amount.parse(*this, str, flags); + } -struct compare_amount_commodities { - bool operator()(const amount_t * left, const amount_t * right) const; + amount_t parse_amount(std::istream& in, amount_t::flags_t flags = 0) { + amount_t temp; + parse_amount(temp, in, flags); + return temp; + } + amount_t parse_amount(const string& str, amount_t::flags_t flags = 0) { + amount_t temp; + parse_amount(temp, str, flags); + return temp; + } }; } // namespace ledger diff --git a/src/gnucash.cc b/src/gnucash.cc index 0fb62bf0..50b1f8f8 100644 --- a/src/gnucash.cc +++ b/src/gnucash.cc @@ -191,7 +191,7 @@ void dataHandler(void *userData, const char *s, int len) string symbol(s, len); if (symbol == "USD") symbol = "$"; - parser->curr_comm = commodity_t::find_or_create(symbol); + parser->curr_comm = amount_t::default_pool->find_or_create(symbol); assert(parser->curr_comm); if (symbol != "$") @@ -320,7 +320,7 @@ unsigned int gnucash_parser_t::parse(std::istream& in, // GnuCash uses the USD commodity without defining it, which really // means $. - commodity_t * usd = commodity_t::find_or_create("$"); + commodity_t * usd = amount_t::default_pool->find_or_create("$"); usd->set_precision(2); usd->add_flags(COMMODITY_STYLE_THOUSANDS); diff --git a/src/journal.cc b/src/journal.cc index 109d07f4..7674bd4d 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -115,8 +115,9 @@ bool entry_base_t::finalize() annotated_commodity_t& ann_comm(static_cast<annotated_commodity_t&> ((*x)->amount->commodity())); - if (ann_comm.price) - balance += *ann_comm.price * (*x)->amount->number() - *((*x)->cost); + if (ann_comm.details.price) + balance += (*ann_comm.details.price * (*x)->amount->number() - + *((*x)->cost)); } } else { saw_null = true; @@ -173,9 +174,9 @@ bool entry_base_t::finalize() if ((*x)->amount->commodity() && ! (*x)->amount->commodity().annotated) (*x)->amount->annotate_commodity - (per_unit_cost.abs(), - entry ? entry->actual_date() : optional<moment_t>(), - entry ? entry->code : optional<string>()); + (annotation_t(per_unit_cost.abs(), + entry ? entry->actual_date() : optional<moment_t>(), + entry ? entry->code : optional<string>())); (*x)->cost = - (per_unit_cost * (*x)->amount->number()); balance += *(*x)->cost; @@ -610,14 +611,6 @@ bool journal_t::valid() const return false; } - for (commodities_map::const_iterator i = commodity_t::commodities.begin(); - i != commodity_t::commodities.end(); - i++) - if (! (*i).second->valid()) { - DEBUG("ledger.validate", "journal_t: commodity not valid"); - return false; - } - return true; } diff --git a/src/main.cc b/src/main.cc index ae3d0d46..25a5fc18 100644 --- a/src/main.cc +++ b/src/main.cc @@ -408,26 +408,26 @@ int main(int argc, char * argv[], char * envp[]) #if defined(TRACING_ON) if (i + 1 < argc && std::strcmp(argv[i], "--trace") == 0) { ledger::_log_level = LOG_TRACE; - ledger::_trace_level = lexical_cast<int>(argv[i + 1]); + ledger::_trace_level = std::atoi(argv[i + 1]); i++; } #endif } + IF_VERIFY() + initialize_memory_tracing(); + try { std::ios::sync_with_stdio(false); boost::filesystem::path::default_name_check (boost::filesystem::portable_posix_name); - ledger::initialize(); - -#if ! defined(FULL_DEBUG) - ledger::do_cleanup = false; -#endif INFO("Ledger starting"); std::auto_ptr<ledger::session_t> session(new ledger::session_t); + ledger::set_session_context(session.get()); + #if 0 session->register_parser(new binary_parser_t); #endif @@ -445,9 +445,11 @@ int main(int argc, char * argv[], char * envp[]) status = read_and_report(report.get(), argc, argv, envp); - IF_VERIFY() { + if (! DO_VERIFY()) { report.release(); session.release(); + } else { + ledger::set_session_context(); } } #if 0 @@ -480,8 +482,12 @@ int main(int argc, char * argv[], char * envp[]) status = _status; } - IF_VERIFY() - ledger::shutdown(); + IF_VERIFY() { + INFO("Ledger ended (Boost/libstdc++ may still hold memory)"); + shutdown_memory_tracing(); + } else { + INFO("Ledger ended"); + } return status; } @@ -110,7 +110,7 @@ unsigned int qif_parser_t::parse(std::istream& in, unsigned char prec = xact->amount->commodity().precision(); if (! def_commodity) { - def_commodity = commodity_t::find_or_create("$"); + def_commodity = amount_t::default_pool->find_or_create("$"); assert(def_commodity); } xact->amount->set_commodity(*def_commodity); diff --git a/src/quotes.cc b/src/quotes.cc index 21ed5998..d40106f3 100644 --- a/src/quotes.cc +++ b/src/quotes.cc @@ -2,31 +2,36 @@ namespace ledger { -void quotes_by_script::operator()(commodity_base_t& commodity, - const ptime& moment, - const ptime& date, - const ptime& last, - amount_t& price) +optional<amount_t> +quotes_by_script::operator()(commodity_t& commodity, + const optional<moment_t>& date, + const optional<moment_t>& moment, + const optional<moment_t>& last) { LOGGER("quotes.download"); - DEBUG_("commodity: " << commodity.symbol); - DEBUG_(" now: " << now); - DEBUG_(" moment: " << moment); - DEBUG_(" date: " << date); - DEBUG_(" last: " << last); - - if (SHOW_DEBUG_() && commodity.history) - DEBUG_("last_lookup: " << commodity.history->last_lookup); + IF_DEBUG_() { + DEBUG_("commodity: " << commodity.symbol()); + DEBUG_(" now: " << now); + if (date) + DEBUG_(" date: " << date); + if (moment) + DEBUG_(" moment: " << moment); + if (last) + DEBUG_(" last: " << last); + if (commodity.history()) + DEBUG_("last_lookup: " << commodity.history()->last_lookup); + } DEBUG_("pricing_leeway is " << pricing_leeway); - if ((commodity.history && - (time_now - commodity.history->last_lookup) < pricing_leeway) || - (time_now - last) < pricing_leeway || - (price && moment > date && (moment - date) <= pricing_leeway)) - return; + if ((commodity.history() && + (now - commodity.history()->last_lookup) < pricing_leeway) || + (last && (now - *last) < pricing_leeway) || + (moment && date && *moment > *date && + (*moment - *date) <= pricing_leeway)) + return optional<amount_t>(); - DEBUG_("downloading quote for symbol " << commodity.symbol); + DEBUG_("downloading quote for symbol " << commodity.symbol()); char buf[256]; buf[0] = '\0'; @@ -34,7 +39,7 @@ void quotes_by_script::operator()(commodity_base_t& commodity, bool success = true; if (FILE * fp = popen((string("getquote \"") + - commodity.symbol + "\"").c_str(), "r")) { + commodity.base_symbol() + "\"").c_str(), "r")) { if (feof(fp) || ! fgets(buf, 255, fp)) success = false; if (pclose(fp) != 0) @@ -49,31 +54,31 @@ void quotes_by_script::operator()(commodity_base_t& commodity, DEBUG_("downloaded quote: " << buf); + amount_t price; price.parse(buf); commodity.add_price(now, price); - commodity.history->last_lookup = time_now; + commodity.history()->last_lookup = now; cache_dirty = true; - if (price) { - assert(! price_db.empty()); + assert(! price_db.empty()); #if defined(__GNUG__) && __GNUG__ < 3 - ofstream database(price_db, ios::out | ios::app); + ofstream database(price_db, ios::out | ios::app); #else - ofstream database(price_db, std::ios_base::out | std::ios_base::app); + ofstream database(price_db, std::ios_base::out | std::ios_base::app); #endif #if 0 - // jww (2007-04-18): Need to convert to local time and print - // here, print with UTC timezone specifier - database << "P " << now.to_string("%Y/%m/%d %H:%M:%S") - << " " << commodity.symbol << " " << price << endl; + // jww (2007-04-18): Need to convert to local time and print + // here, print with UTC timezone specifier + database << "P " << now.to_string("%Y/%m/%d %H:%M:%S") + << " " << commodity.symbol << " " << price << endl; #endif - } + return price; } else { throw_(download_error, - "Failed to download price for '" << commodity.symbol << - "' (command: \"getquote " << commodity.symbol << "\")"); + "Failed to download price for '" << commodity.symbol() << + "' (command: \"getquote " << commodity.base_symbol() << "\")"); } } diff --git a/src/quotes.h b/src/quotes.h index 8ac3b9d4..5f39f041 100644 --- a/src/quotes.h +++ b/src/quotes.h @@ -5,7 +5,7 @@ namespace ledger { -class quotes_by_script : public commodity_base_t::updater_t +class quotes_by_script { path price_db; time_duration pricing_leeway; @@ -18,11 +18,11 @@ class quotes_by_script : public commodity_base_t::updater_t : price_db(_price_db), pricing_leeway(_pricing_leeway), cache_dirty(_cache_dirty) {} - virtual void operator()(commodity_base_t& commodity, - const ptime& moment, - const ptime& date, - const ptime& last, - amount_t& price); + virtual optional<amount_t> + operator()(commodity_t& commodity, + const optional<moment_t>& date, + const optional<moment_t>& moment, + const optional<moment_t>& last); }; DECLARE_EXCEPTION(download_error); diff --git a/src/session.cc b/src/session.cc index 5f452f25..d53faf09 100644 --- a/src/session.cc +++ b/src/session.cc @@ -2,6 +2,41 @@ namespace ledger { +session_t * session_t::current = NULL; + +#if 0 +boost::mutex session_t::session_mutex; +#endif + +static void initialize(); +static void shutdown(); + +void set_session_context(session_t * session) +{ +#if 0 + session_t::session_mutex.lock(); +#endif + + if (session && ! session_t::current) { + initialize(); + } + else if (! session && session_t::current) { + shutdown(); +#if 0 + session_t::session_mutex.unlock(); +#endif + } + + session_t::current = session; +} + +void release_session_context() +{ +#if 0 + session_t::session_mutex.unlock(); +#endif +} + unsigned int session_t::read_journal(std::istream& in, journal_t * journal, account_t * master, @@ -203,26 +238,16 @@ xml::xpath_t::op_t * session_t::lookup(const string& name) // jww (2007-04-26): All of Ledger should be accessed through a // session_t object -void initialize() +static void initialize() { - IF_VERIFY() - initialize_memory_tracing(); - amount_t::initialize(); xml::xpath_t::initialize(); } -void shutdown() +static void shutdown() { xml::xpath_t::shutdown(); amount_t::shutdown(); - - IF_VERIFY() { - INFO("Ledger ended (Boost/libstdc++ may still hold memory)"); - shutdown_memory_tracing(); - } else { - INFO("Ledger ended"); - } } } // namespace ledger diff --git a/src/session.h b/src/session.h index 6fb2c21d..c8d83065 100644 --- a/src/session.h +++ b/src/session.h @@ -10,6 +10,8 @@ namespace ledger { class session_t : public xml::xpath_t::scope_t { public: + static session_t * current; + path data_file; optional<path> init_file; optional<path> cache_file; @@ -185,8 +187,16 @@ class session_t : public xml::xpath_t::scope_t #endif }; -void initialize(); -void shutdown(); +/** + * This sets the current session context, transferring all static + * globals to point at the data structures related to this session. + * Although Ledger itself is not thread-safe, by locking, switching + * session context, then unlocking after the operation is done, + * multiple threads can sequentially make use of the library. Thus, a + * session_t maintains all of the information relating to a single + * usage of the Ledger library. + */ +void set_session_context(session_t * session = NULL); } // namespace ledger diff --git a/src/system.hh b/src/system.hh index 34319d75..2cc03413 100644 --- a/src/system.hh +++ b/src/system.hh @@ -98,7 +98,7 @@ extern "C" { #endif #include <boost/algorithm/string/predicate.hpp> -#include <boost/any.hpp> +#include <boost/cast.hpp> #include <boost/current_function.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/filesystem/convenience.hpp> @@ -106,11 +106,16 @@ extern "C" { #include <boost/filesystem/fstream.hpp> #include <boost/filesystem/operations.hpp> #include <boost/filesystem/path.hpp> -#include <boost/lexical_cast.hpp> +#include <boost/function.hpp> +#include <boost/multi_index_container.hpp> +#include <boost/multi_index/hashed_index.hpp> +#include <boost/multi_index/key_extractors.hpp> +#include <boost/multi_index/ordered_index.hpp> #include <boost/operators.hpp> #include <boost/optional.hpp> #include <boost/ptr_container/ptr_list.hpp> #include <boost/ptr_container/ptr_vector.hpp> #include <boost/regex.hpp> +#include <boost/signal.hpp> #endif // _SYSTEM_HH diff --git a/src/textual.cc b/src/textual.cc index 5fe13e6d..6b36a4ac 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -263,9 +263,9 @@ transaction_t * parse_transaction(char * line, if (xact->amount->commodity() && ! xact->amount->commodity().annotated) - xact->amount->annotate_commodity(per_unit_cost, - xact->entry->actual_date(), - xact->entry->code); + xact->amount->annotate_commodity(annotation_t(per_unit_cost, + xact->entry->actual_date(), + xact->entry->code)); DEBUG("ledger.textual.parse", "line " << linenum << ": " << "Total cost is " << *xact->cost); @@ -718,7 +718,7 @@ unsigned int textual_parser_t::parse(std::istream& in, case 'D': { // a default commodity for "entry" amount_t amt(skip_ws(line + 1)); - commodity_t::default_commodity = &amt.commodity(); + amount_t::default_pool->default_commodity = &amt.commodity(); break; } @@ -756,7 +756,8 @@ unsigned int textual_parser_t::parse(std::istream& in, parse_symbol(symbol_and_price, symbol); amount_t price(symbol_and_price); - if (commodity_t * commodity = commodity_t::find_or_create(symbol)) + if (commodity_t * commodity = + amount_t::default_pool->find_or_create(symbol)) commodity->add_price(datetime, price); break; } @@ -766,7 +767,8 @@ unsigned int textual_parser_t::parse(std::istream& in, string symbol; parse_symbol(p, symbol); - if (commodity_t * commodity = commodity_t::find_or_create(symbol)) + if (commodity_t * commodity = + amount_t::default_pool->find_or_create(symbol)) commodity->add_flags(COMMODITY_STYLE_NOMARKET); break; } @@ -774,7 +776,7 @@ unsigned int textual_parser_t::parse(std::istream& in, case 'Y': // set current year #if 0 // jww (2007-04-18): Need to set this up again - date_t::current_year = lexical_cast<int>(skip_ws(line + 1)); + date_t::current_year = std::atoi(skip_ws(line + 1)); #endif break; diff --git a/src/utils.h b/src/utils.h index 65071035..4a7213b7 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,6 +1,11 @@ #ifndef _UTILS_H #define _UTILS_H +#if defined(FULL_DEBUG) +#define BOOST_MULTI_INDEX_ENABLE_SAFE_MODE 1 +#define BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING 1 +#endif + #include <system.hh> /********************************************************************** diff --git a/src/value.cc b/src/value.cc index b181df8f..e1cfa736 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1647,7 +1647,7 @@ void value_t::in_place_cast(type_t cast_type) break; } if (alldigits) { - long temp = lexical_cast<long>((*(string **) data)->c_str()); + long temp = std::atol((*(string **) data)->c_str()); destroy(); *(long *) data = temp; } else { @@ -1840,7 +1840,7 @@ bool value_t::realzero() const return 0; } -value_t value_t::value(const moment_t& moment) const +value_t value_t::value(const optional<moment_t>& moment) const { switch (type) { case BOOLEAN: @@ -1849,16 +1849,30 @@ value_t value_t::value(const moment_t& moment) const throw_(value_error, "Cannot find the value of a date/time"); case INTEGER: return *this; - case AMOUNT: - return ((amount_t *) data)->value(moment); - case BALANCE: - return ((balance_t *) data)->value(moment); - case BALANCE_PAIR: - return ((balance_pair_t *) data)->quantity.value(moment); + + case AMOUNT: { + if (optional<amount_t> val = ((amount_t *) data)->value(moment)) + return *val; + return false; + } + case BALANCE: { + if (optional<balance_t> bal = ((balance_t *) data)->value(moment)) + return *bal; + return false; + } + case BALANCE_PAIR: { + if (optional<balance_t> bal = + ((balance_pair_t *) data)->quantity.value(moment)) + return *bal; + return false; + } + case STRING: throw_(value_error, "Cannot find the value of a string"); + case XML_NODE: return (*(xml::node_t **) data)->to_value().value(moment); + case POINTER: throw_(value_error, "Cannot find the value of a pointer"); case SEQUENCE: @@ -1954,45 +1968,37 @@ value_t value_t::unround() const return value_t(); } -value_t value_t::price() const +value_t value_t::annotated_price() const { switch (type) { case BOOLEAN: - throw_(value_error, "Cannot find the price of a boolean"); + throw_(value_error, "Cannot find the annotated price of a boolean"); case INTEGER: return *this; case DATETIME: - throw_(value_error, "Cannot find the price of a date/time"); + throw_(value_error, "Cannot find the annotated price of a date/time"); case AMOUNT: { - optional<amount_t> temp = ((amount_t *) data)->price(); - if (! temp) - return false; - return *temp; - } - case BALANCE: { - optional<balance_t> temp = ((balance_t *) data)->price(); - if (! temp) - return false; - return *temp; - } - case BALANCE_PAIR: { - optional<balance_t> temp = ((balance_pair_t *) data)->price(); + optional<amount_t> temp = ((amount_t *) data)->annotation_details().price; if (! temp) return false; return *temp; } + case BALANCE: + throw_(value_error, "Cannot find the annotated price of a balance"); + case BALANCE_PAIR: + throw_(value_error, "Cannot find the annotated price of a balance pair"); case STRING: - throw_(value_error, "Cannot find the price of a string"); + throw_(value_error, "Cannot find the annotated price of a string"); case XML_NODE: - return (*(xml::node_t **) data)->to_value().price(); + return (*(xml::node_t **) data)->to_value().annotated_price(); case POINTER: - throw_(value_error, "Cannot find the price of a pointer"); + throw_(value_error, "Cannot find the annotated price of a pointer"); case SEQUENCE: - throw_(value_error, "Cannot find the price of a sequence"); + throw_(value_error, "Cannot find the annotated price of a sequence"); default: assert(0); @@ -2002,46 +2008,38 @@ value_t value_t::price() const return value_t(); } -value_t value_t::date() const +value_t value_t::annotated_date() const { switch (type) { case BOOLEAN: - throw_(value_error, "Cannot find the date of a boolean"); + throw_(value_error, "Cannot find the annotated date of a boolean"); case INTEGER: - throw_(value_error, "Cannot find the date of an integer"); + throw_(value_error, "Cannot find the annotated date of an integer"); case DATETIME: return *this; case AMOUNT: { - optional<moment_t> temp = ((amount_t *) data)->date(); - if (! temp) - return false; - return *temp; - } - case BALANCE: { - optional<moment_t> temp = ((balance_t *) data)->date(); - if (! temp) - return false; - return *temp; - } - case BALANCE_PAIR: { - optional<moment_t> temp = ((balance_pair_t *) data)->date(); + optional<moment_t> temp = ((amount_t *) data)->annotation_details().date; if (! temp) return false; return *temp; } + case BALANCE: + throw_(value_error, "Cannot find the annotated date of a balance"); + case BALANCE_PAIR: + throw_(value_error, "Cannot find the annotated date of a balance pair"); case STRING: - throw_(value_error, "Cannot find the date of a string"); + throw_(value_error, "Cannot find the annotated date of a string"); case XML_NODE: - return (*(xml::node_t **) data)->to_value().date(); + return (*(xml::node_t **) data)->to_value().annotated_date(); case POINTER: - throw_(value_error, "Cannot find the date of a pointer"); + throw_(value_error, "Cannot find the annotated date of a pointer"); case SEQUENCE: - throw_(value_error, "Cannot find the date of a sequence"); + throw_(value_error, "Cannot find the annotated date of a sequence"); default: assert(0); @@ -2051,46 +2049,38 @@ value_t value_t::date() const return value_t(); } -value_t value_t::tag() const +value_t value_t::annotated_tag() const { switch (type) { case BOOLEAN: - throw_(value_error, "Cannot find the date of a boolean"); + throw_(value_error, "Cannot find the annotated tag of a boolean"); case INTEGER: - throw_(value_error, "Cannot find the date of an integer"); + throw_(value_error, "Cannot find the annotated tag of an integer"); case DATETIME: return *this; case AMOUNT: { - optional<string> temp = ((amount_t *) data)->tag(); - if (! temp) - return false; - return *temp; - } - case BALANCE: { - optional<string> temp = ((balance_t *) data)->tag(); - if (! temp) - return false; - return *temp; - } - case BALANCE_PAIR: { - optional<string> temp = ((balance_pair_t *) data)->tag(); + optional<string> temp = ((amount_t *) data)->annotation_details().tag; if (! temp) return false; return *temp; } + case BALANCE: + throw_(value_error, "Cannot find the annotated tag of a balance"); + case BALANCE_PAIR: + throw_(value_error, "Cannot find the annotated tag of a balance pair"); case STRING: - throw_(value_error, "Cannot find the date of a string"); + throw_(value_error, "Cannot find the annotated tag of a string"); case XML_NODE: - return (*(xml::node_t **) data)->to_value().date(); + return (*(xml::node_t **) data)->to_value().annotated_tag(); case POINTER: - throw_(value_error, "Cannot find the date of a pointer"); + throw_(value_error, "Cannot find the annotated tag of a pointer"); case SEQUENCE: - throw_(value_error, "Cannot find the date of a sequence"); + throw_(value_error, "Cannot find the annotated tag of a sequence"); default: assert(0); diff --git a/src/value.h b/src/value.h index b44cdd33..d8c18fe4 100644 --- a/src/value.h +++ b/src/value.h @@ -363,9 +363,9 @@ class value_t value_t abs() const; void in_place_cast(type_t cast_type); value_t cost() const; - value_t price() const; - value_t date() const; - value_t tag() const; + value_t annotated_price() const; + value_t annotated_date() const; + value_t annotated_tag() const; value_t cast(type_t cast_type) const { value_t temp(*this); @@ -379,7 +379,8 @@ class value_t value_t& add(const amount_t& amount, const optional<amount_t>& cost = optional<amount_t>()); - value_t value(const moment_t& moment) const; + value_t value(const optional<moment_t>& moment = + optional<moment_t>()) const; void in_place_reduce(); value_t reduce() const { diff --git a/src/xmlparse.cc b/src/xmlparse.cc index 94b76ad7..c54e5969 100644 --- a/src/xmlparse.cc +++ b/src/xmlparse.cc @@ -104,7 +104,7 @@ static void endElement(void *userData, const char *name) } else if (std::strcmp(name, "symbol") == 0) { assert(! curr_comm); - curr_comm = commodity_t::find_or_create(data); + curr_comm = amount_t::default_pool->find_or_create(data); assert(curr_comm); curr_comm->add_flags(COMMODITY_STYLE_SUFFIXED); if (! comm_flags.empty()) { diff --git a/src/xpath.cc b/src/xpath.cc index a7e1e661..fd574cee 100644 --- a/src/xpath.cc +++ b/src/xpath.cc @@ -654,7 +654,7 @@ xpath_t::parse_value_term(std::istream& in, unsigned short tflags) const int id = -1; if (std::isdigit(ident[0])) { node.reset(new op_t(op_t::ARG_INDEX)); - node->arg_index = lexical_cast<unsigned int>(ident.c_str()); + node->arg_index = std::atol(ident.c_str()); } else if ((id = document_t::lookup_builtin_id(ident)) != -1) { node.reset(new op_t(op_t::NODE_ID)); @@ -2286,7 +2286,7 @@ bool xpath_t::op_t::write(std::ostream& out, } if (! symbol.empty()) { - if (commodity_t::find(symbol)) + if (amount_t::default_pool->find(symbol)) out << '@'; out << symbol; } diff --git a/tests/numerics/BasicAmount.cc b/tests/numerics/BasicAmount.cc index de249ae5..aed682a6 100644 --- a/tests/numerics/BasicAmount.cc +++ b/tests/numerics/BasicAmount.cc @@ -3,10 +3,10 @@ CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(BasicAmountTestCase, "numerics"); void BasicAmountTestCase::setUp() { - ledger::initialize(); + ledger::set_session_context(&session); } void BasicAmountTestCase::tearDown() { - ledger::shutdown(); + ledger::set_session_context(); } void BasicAmountTestCase::testConstructors() @@ -17,8 +17,8 @@ void BasicAmountTestCase::testConstructors() amount_t x3(123.456); amount_t x5("123456"); amount_t x6("123.456"); - amount_t x7(string("123456")); - amount_t x8(string("123.456")); + amount_t x7(std::string("123456")); + amount_t x8(std::string("123.456")); amount_t x9(x3); amount_t x10(x6); amount_t x11(x8); diff --git a/tests/numerics/BasicAmount.h b/tests/numerics/BasicAmount.h index 12449e49..4d11821e 100644 --- a/tests/numerics/BasicAmount.h +++ b/tests/numerics/BasicAmount.h @@ -33,6 +33,8 @@ class BasicAmountTestCase : public CPPUNIT_NS::TestCase CPPUNIT_TEST_SUITE_END(); public: + ledger::session_t session; + BasicAmountTestCase() {} virtual ~BasicAmountTestCase() {} diff --git a/tests/numerics/Commodity.cc b/tests/numerics/Commodity.cc index e7e2a18c..25da9d42 100644 --- a/tests/numerics/Commodity.cc +++ b/tests/numerics/Commodity.cc @@ -3,10 +3,10 @@ CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(CommodityTestCase, "numerics"); void CommodityTestCase::setUp() { - ledger::initialize(); + ledger::set_session_context(&session); } void CommodityTestCase::tearDown() { - ledger::shutdown(); + ledger::set_session_context(); } void CommodityTestCase::testPriceHistory() @@ -32,8 +32,13 @@ void CommodityTestCase::testPriceHistory() aapl.add_price(mar01_07, amount_t("$19.50")); aapl.add_price(apr15_07, amount_t("$21.22")); - assertEqual(amount_t("$1831.83"), x1.value(feb28_07sbm)); - assertEqual(amount_t("$2124.12"), x1.value(now)); + optional<amount_t> amt1 = x1.value(feb28_07sbm); + assertTrue(amt1); + assertEqual(amount_t("$1831.83"), *amt1); + + optional<amount_t> amt2 = x1.value(now); + assertTrue(amt2); + assertEqual(amount_t("$2124.12"), *amt2); assertValid(x1); } diff --git a/tests/numerics/Commodity.h b/tests/numerics/Commodity.h index dafa03e9..aebe9a9e 100644 --- a/tests/numerics/Commodity.h +++ b/tests/numerics/Commodity.h @@ -15,6 +15,8 @@ class CommodityTestCase : public CPPUNIT_NS::TestCase CPPUNIT_TEST_SUITE_END(); public: + ledger::session_t session; + CommodityTestCase() {} virtual ~CommodityTestCase() {} diff --git a/tests/numerics/CommodityAmount.cc b/tests/numerics/CommodityAmount.cc index e443d6a1..3b38ea96 100644 --- a/tests/numerics/CommodityAmount.cc +++ b/tests/numerics/CommodityAmount.cc @@ -6,7 +6,7 @@ CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(CommodityAmountTestCase, "numerics"); void CommodityAmountTestCase::setUp() { - ledger::initialize(); + ledger::set_session_context(&session); // Cause the display precision for dollars to be initialized to 2. amount_t x1("$1.00"); @@ -17,7 +17,8 @@ void CommodityAmountTestCase::setUp() void CommodityAmountTestCase::tearDown() { amount_t::full_strings = false; - ledger::shutdown(); + + ledger::set_session_context(); } void CommodityAmountTestCase::testConstructors() diff --git a/tests/numerics/CommodityAmount.h b/tests/numerics/CommodityAmount.h index 5ffa7810..d80a3e15 100644 --- a/tests/numerics/CommodityAmount.h +++ b/tests/numerics/CommodityAmount.h @@ -28,6 +28,8 @@ class CommodityAmountTestCase : public CPPUNIT_NS::TestCase CPPUNIT_TEST_SUITE_END(); public: + ledger::session_t session; + CommodityAmountTestCase() {} virtual ~CommodityAmountTestCase() {} |