Revised how commodities are dealt with.

This commit is contained in:
John Wiegley 2007-05-03 06:11:04 +00:00
parent f9f24fab93
commit c59018c29d
31 changed files with 1146 additions and 915 deletions

View file

@ -172,7 +172,7 @@ ledger_so_SOURCES = src/pyledger.cc
ledger_so_DEPENDENCIES = libledger.la gdtoa/libgdtoa.la libpyledger.la ledger_so_DEPENDENCIES = libledger.la gdtoa/libgdtoa.la libpyledger.la
PYLIBS = pyledger ledger gdtoa gmp boost_date_time \ 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 if HAVE_EXPAT
PYLIBS += expat PYLIBS += expat

View file

@ -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_SOURCES = src/pyledger.cc
@HAVE_BOOST_PYTHON_TRUE@ledger_so_DEPENDENCIES = libledger.la gdtoa/libgdtoa.la libpyledger.la @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@PYLIBS = pyledger ledger gdtoa gmp \
@HAVE_BOOST_PYTHON_TRUE@ boost_date_time boost_filesystem \ @HAVE_BOOST_PYTHON_TRUE@ boost_date_time boost_signals \
@HAVE_BOOST_PYTHON_TRUE@ boost_regex boost_python \ @HAVE_BOOST_PYTHON_TRUE@ boost_filesystem boost_regex \
@HAVE_BOOST_PYTHON_TRUE@ $(am__append_15) $(am__append_16) \ @HAVE_BOOST_PYTHON_TRUE@ boost_python $(am__append_15) \
@HAVE_BOOST_PYTHON_TRUE@ $(am__append_17) @HAVE_BOOST_PYTHON_TRUE@ $(am__append_16) $(am__append_17)
nodist_UnitTests_SOURCES = tests/UnitTests.cc \ nodist_UnitTests_SOURCES = tests/UnitTests.cc \
\ \
tests/numerics/BasicAmount.cc \ tests/numerics/BasicAmount.cc \

98
configure vendored
View file

@ -1,6 +1,6 @@
#! /bin/sh #! /bin/sh
# Guess values for system-dependent variables and create Makefiles. # 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>. # Report bugs to <johnw@newartisans.com>.
# #
@ -728,8 +728,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
# Identity of this package. # Identity of this package.
PACKAGE_NAME='ledger' PACKAGE_NAME='ledger'
PACKAGE_TARNAME='ledger' PACKAGE_TARNAME='ledger'
PACKAGE_VERSION='3.0-git-lexical_cast' PACKAGE_VERSION='3.0-git-commodity_pool'
PACKAGE_STRING='ledger 3.0-git-lexical_cast' PACKAGE_STRING='ledger 3.0-git-commodity_pool'
PACKAGE_BUGREPORT='johnw@newartisans.com' PACKAGE_BUGREPORT='johnw@newartisans.com'
ac_unique_file="ledger" 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. # 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. # This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF 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]... Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1494,7 +1494,7 @@ fi
if test -n "$ac_init_help"; then if test -n "$ac_init_help"; then
case $ac_init_help in 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 esac
cat <<\_ACEOF cat <<\_ACEOF
@ -1605,7 +1605,7 @@ fi
test -n "$ac_init_help" && exit $ac_status test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then if $ac_init_version; then
cat <<\_ACEOF cat <<\_ACEOF
ledger configure 3.0-git-lexical_cast ledger configure 3.0-git-commodity_pool
generated by GNU Autoconf 2.61 generated by GNU Autoconf 2.61
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 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 This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake. 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 generated by GNU Autoconf 2.61. Invocation command line was
$ $0 $@ $ $0 $@
@ -2310,7 +2310,7 @@ fi
# Define the identity of the package. # Define the identity of the package.
PACKAGE='ledger' PACKAGE='ledger'
VERSION='3.0-git-lexical_cast' VERSION='3.0-git-commodity_pool'
cat >>confdefs.h <<_ACEOF cat >>confdefs.h <<_ACEOF
@ -19691,6 +19691,84 @@ See \`config.log' for more details." >&2;}
{ (exit 1); exit 1; }; } { (exit 1); exit 1; }; }
fi 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 # check for gmp
{ echo "$as_me:$LINENO: checking if libgmp is available" >&5 { echo "$as_me:$LINENO: checking if libgmp is available" >&5
echo $ECHO_N "checking if libgmp is available... $ECHO_C" >&6; } 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 # report actual input values of CONFIG_FILES etc. instead of their
# values after options handling. # values after options handling.
ac_log=" 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 generated by GNU Autoconf 2.61. Invocation command line was
CONFIG_FILES = $CONFIG_FILES CONFIG_FILES = $CONFIG_FILES
@ -22020,7 +22098,7 @@ Report bugs to <bug-autoconf@gnu.org>."
_ACEOF _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF cat >>$CONFIG_STATUS <<_ACEOF
ac_cs_version="\\ 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, configured by $0, generated by GNU Autoconf 2.61,
with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"

View file

@ -3,7 +3,7 @@
AC_PREREQ(2.59) 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) AC_CONFIG_SRCDIR(ledger)
AM_INIT_AUTOMAKE AM_INIT_AUTOMAKE
@ -148,6 +148,27 @@ else
AC_MSG_FAILURE("Could not find boost_filesystem library (set CPPFLAGS and LDFLAGS?)") AC_MSG_FAILURE("Could not find boost_filesystem library (set CPPFLAGS and LDFLAGS?)")
fi 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 # check for gmp
AC_CACHE_CHECK( AC_CACHE_CHECK(
[if libgmp is available], [if libgmp is available],

View file

@ -46,6 +46,8 @@
namespace ledger { namespace ledger {
commodity_pool_t * amount_t::default_pool = NULL;
bool amount_t::keep_base = false; bool amount_t::keep_base = false;
bool amount_t::keep_price = false; bool amount_t::keep_price = false;
@ -109,25 +111,12 @@ void amount_t::initialize()
mpz_init(temp); mpz_init(temp);
mpz_init(divisor); 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; true_value = new amount_t::bigint_t;
mpz_set_ui(true_value->val, 1); 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() void amount_t::shutdown()
@ -135,30 +124,12 @@ void amount_t::shutdown()
mpz_clear(temp); mpz_clear(temp);
mpz_clear(divisor); mpz_clear(divisor);
if (commodity_base_t::updater) { // jww (2007-05-02): Be very careful here!
checked_delete(commodity_base_t::updater); if (default_pool) {
commodity_base_t::updater = NULL; 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--; true_value->ref--;
assert(true_value->ref == 0); assert(true_value->ref == 0);
checked_delete(true_value); checked_delete(true_value);
@ -408,9 +379,9 @@ amount_t& amount_t::operator+=(const amount_t& amt)
if (commodity() != amt.commodity()) if (commodity() != amt.commodity())
throw_(amount_error, throw_(amount_error,
"Adding amounts with different commodities: " << "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) if (! amt.quantity)
return *this; return *this;
@ -443,9 +414,9 @@ amount_t& amount_t::operator-=(const amount_t& amt)
if (commodity() != amt.commodity()) if (commodity() != amt.commodity())
throw_(amount_error, throw_(amount_error,
"Subtracting amounts with different commodities: " << "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) if (! amt.quantity)
return *this; return *this;
@ -529,9 +500,9 @@ amount_t& amount_t::operator*=(const amount_t& amt)
commodity() != amt.commodity()) commodity() != amt.commodity())
throw_(amount_error, throw_(amount_error,
"Multiplying amounts with different commodities: " << "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) { if (! amt.quantity) {
*this = *this - *this; // preserve our commodity *this = *this - *this; // preserve our commodity
@ -569,9 +540,9 @@ amount_t& amount_t::operator/=(const amount_t& amt)
commodity() != amt.commodity()) commodity() != amt.commodity())
throw_(amount_error, throw_(amount_error,
"Dividing amounts with different commodities: " << "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) { if (! amt.quantity || ! amt) {
throw_(amount_error, "Divide by zero"); throw_(amount_error, "Divide by zero");
@ -686,14 +657,14 @@ amount_t& amount_t::in_place_unreduce()
return *this; 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) { if (quantity) {
amount_t amt(commodity().value(moment)); optional<amount_t> amt(commodity().value(moment));
if (! amt.realzero()) if (amt)
return (amt * number()).round(); 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, void amount_t::annotate_commodity(const annotation_t& details)
const optional<moment_t>& tdate,
const optional<string>& ttag)
{ {
const commodity_t * this_base; commodity_t * this_base;
annotated_commodity_t * this_ann = NULL; annotated_commodity_t * this_ann = NULL;
if (commodity().annotated) { if (commodity().annotated) {
this_ann = &static_cast<annotated_commodity_t&>(commodity()); this_ann = &commodity().as_annotated();
this_base = this_ann->ptr; this_base = &this_ann->referent();
} else { } else {
this_base = &commodity(); this_base = &commodity();
} }
assert(this_base); assert(this_base);
DEBUG("amounts.commodities", "Annotating commodity for amount " DEBUG("amounts.commodities", "Annotating commodity for amount "
<< *this << std::endl << *this << std::endl << details);
<< " price " << (tprice ? tprice->to_string() : "NONE") << " "
<< " date " << (tdate ? *tdate : moment_t()) << " "
<< " ttag " << (ttag ? *ttag : "NONE"));
if (commodity_t * ann_comm = if (commodity_t * ann_comm =
annotated_commodity_t::find_or_create this_base->parent().find_or_create(*this_base, details))
(*this_base,
! tprice && this_ann ? this_ann->price : tprice,
! tdate && this_ann ? this_ann->date : tdate,
! ttag && this_ann ? this_ann->tag : ttag))
set_commodity(*ann_comm); set_commodity(*ann_comm);
#ifdef ASSERTS_ON
else
assert(false);
#endif
DEBUG("amounts.commodities", " Annotated amount is " << *this); 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 date " << _keep_date << " "
<< " keep tag " << _keep_tag); << " keep tag " << _keep_tag);
annotated_commodity_t& annotated_commodity_t& ann_comm(commodity().as_annotated());
ann_comm(static_cast<annotated_commodity_t&>(commodity())); commodity_t * new_comm;
assert(ann_comm.base);
commodity_t * new_comm; if ((_keep_price && ann_comm.details.price) ||
(_keep_date && ann_comm.details.date) ||
if ((_keep_price && ann_comm.price) || (_keep_tag && ann_comm.details.tag))
(_keep_date && ann_comm.date) ||
(_keep_tag && ann_comm.tag))
{ {
new_comm = annotated_commodity_t::find_or_create new_comm = ann_comm.parent().find_or_create
(*ann_comm.ptr, (ann_comm.referent(),
_keep_price ? ann_comm.price : optional<amount_t>(), annotation_t(_keep_price ? ann_comm.details.price : optional<amount_t>(),
_keep_date ? ann_comm.date : optional<moment_t>(), _keep_date ? ann_comm.details.date : optional<moment_t>(),
_keep_tag ? ann_comm.tag : optional<string>()); _keep_tag ? ann_comm.details.tag : optional<string>()));
} else { } 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); assert(new_comm);
amount_t t(*this); amount_t t(*this);
t.set_commodity(*new_comm); t.set_commodity(*new_comm);
DEBUG("amounts.commodities", " Reduced amount is " << t);
DEBUG("amounts.commodities", " Stripped amount is " << t);
return t; return t;
} }
optional<amount_t> amount_t::price() const bool amount_t::commodity_annotated() const
{ {
if (commodity_ && commodity_->annotated && assert(! commodity().annotated || commodity().as_annotated().details);
((annotated_commodity_t *)commodity_)->price) { return commodity().annotated;
amount_t t(*((annotated_commodity_t *)commodity_)->price);
t *= number();
DEBUG("amounts.commodities",
"Returning price of " << *this << " = " << t);
return t;
}
return optional<amount_t>();
} }
optional<moment_t> amount_t::date() const annotation_t amount_t::annotation_details() const
{ {
if (commodity_ && commodity_->annotated) { assert(! commodity().annotated || commodity().as_annotated().details);
DEBUG("amounts.commodities",
"Returning date of " << *this << " = "
<< ((annotated_commodity_t *)commodity_)->date);
return ((annotated_commodity_t *)commodity_)->date;
}
return optional<moment_t>();
}
optional<string> amount_t::tag() const if (commodity().annotated) {
{ annotated_commodity_t& ann_comm(commodity().as_annotated());
if (commodity_ && commodity_->annotated) { return ann_comm.details;
DEBUG("amounts.commodities",
"Returning tag of " << *this << " = "
<< ((annotated_commodity_t *)commodity_)->tag);
return ((annotated_commodity_t *)commodity_)->tag;
} }
return optional<string>(); return annotation_t();
} }
static void parse_quantity(std::istream& in, string& value) static void parse_quantity(std::istream& in, string& value)
{ {
char buf[256]; char buf[256];
@ -923,16 +868,15 @@ static void parse_commodity(std::istream& in, string& symbol)
symbol = buf; symbol = buf;
} }
void parse_annotations(std::istream& in, bool parse_annotations(commodity_pool_t& parent,
optional<amount_t>& price, std::istream& in,
optional<moment_t>& date, annotation_t& details)
optional<string>& tag)
{ {
do { do {
char buf[256]; char buf[256];
char c = peek_next_nonws(in); char c = peek_next_nonws(in);
if (c == '{') { if (c == '{') {
if (price) if (details.price)
throw_(amount_error, "Commodity specifies more than one price"); throw_(amount_error, "Commodity specifies more than one price");
in.get(c); in.get(c);
@ -943,7 +887,7 @@ void parse_annotations(std::istream& in,
throw_(amount_error, "Commodity price lacks closing brace"); throw_(amount_error, "Commodity price lacks closing brace");
amount_t temp; amount_t temp;
temp.parse(buf, AMOUNT_PARSE_NO_MIGRATE); temp.parse(parent, buf, AMOUNT_PARSE_NO_MIGRATE);
temp.in_place_reduce(); temp.in_place_reduce();
// Since this price will maintain its own precision, make sure // 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.quantity->prec < temp.commodity().precision())
temp = temp.round(); // no need to retain individual precision temp = temp.round(); // no need to retain individual precision
price = temp; details.price = temp;
} }
else if (c == '[') { else if (c == '[') {
if (date) if (details.date)
throw_(amount_error, "Commodity specifies more than one date"); throw_(amount_error, "Commodity specifies more than one date");
in.get(c); in.get(c);
@ -967,10 +911,10 @@ void parse_annotations(std::istream& in,
else else
throw_(amount_error, "Commodity date lacks closing bracket"); throw_(amount_error, "Commodity date lacks closing bracket");
date = parse_datetime(buf); details.date = parse_datetime(buf);
} }
else if (c == '(') { else if (c == '(') {
if (tag) if (details.tag)
throw_(amount_error, "Commodity specifies more than one tag"); throw_(amount_error, "Commodity specifies more than one tag");
in.get(c); in.get(c);
@ -980,7 +924,7 @@ void parse_annotations(std::istream& in,
else else
throw_(amount_error, "Commodity tag lacks closing parenthesis"); throw_(amount_error, "Commodity tag lacks closing parenthesis");
tag = buf; details.tag = buf;
} }
else { else {
break; break;
@ -989,25 +933,28 @@ void parse_annotations(std::istream& in,
DEBUG("amounts.commodities", DEBUG("amounts.commodities",
"Parsed commodity annotations: " "Parsed commodity annotations: "
<< " price " << (price ? price->to_string() : "NONE") << " " << " price "
<< " date " << (date ? *date : moment_t()) << " " << (details.price ? details.price->to_string() : "NONE") << " "
<< " tag " << (tag ? *tag : "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: // The possible syntax for an amount is:
// //
// [-]NUM[ ]SYM [@ AMOUNT] // [-]NUM[ ]SYM [@ AMOUNT]
// SYM[ ][-]NUM [@ AMOUNT] // SYM[ ][-]NUM [@ AMOUNT]
string symbol; string symbol;
string quant; string quant;
optional<amount_t> tprice; annotation_t details;
optional<moment_t> tdate; unsigned int comm_flags = COMMODITY_STYLE_DEFAULTS;
optional<string> ttag; bool negative = false;
unsigned int comm_flags = COMMODITY_STYLE_DEFAULTS;
bool negative = false;
char c = peek_next_nonws(in); char c = peek_next_nonws(in);
if (c == '-') { if (c == '-') {
@ -1030,7 +977,7 @@ void amount_t::parse(std::istream& in, uint8_t flags)
comm_flags |= COMMODITY_STYLE_SUFFIXED; comm_flags |= COMMODITY_STYLE_SUFFIXED;
if (! in.eof() && ((n = in.peek()) != '\n')) if (! in.eof() && ((n = in.peek()) != '\n'))
parse_annotations(in, tprice, tdate, ttag); parse_annotations(parent, in, details);
} }
} else { } else {
parse_commodity(in, symbol); parse_commodity(in, symbol);
@ -1042,7 +989,7 @@ void amount_t::parse(std::istream& in, uint8_t flags)
parse_quantity(in, quant); parse_quantity(in, quant);
if (! quant.empty() && ! in.eof() && ((n = in.peek()) != '\n')) 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()) { if (symbol.empty()) {
commodity_ = NULL; commodity_ = NULL;
} else { } else {
commodity_ = commodity_t::find(symbol); commodity_ = parent.find(symbol);
if (! commodity_) { if (! commodity_) {
commodity_ = commodity_t::create(symbol); commodity_ = parent.create(symbol);
newly_created = true; newly_created = true;
} }
assert(commodity_); assert(commodity_);
if (tprice || tdate || ttag) if (details)
commodity_ = annotated_commodity_t::find_or_create commodity_ = parent.find_or_create(*commodity_, details);
(*commodity_, tprice, tdate, ttag);
} }
// Determine the precision of the amount, based on the usage of // 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 // 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); commodity().add_flags(comm_flags);
if (quantity->prec > commodity().precision()) if (quantity->prec > commodity().precision())
commodity().set_precision(quantity->prec); commodity().set_precision(quantity->prec);
@ -1138,13 +1085,14 @@ void amount_t::parse(std::istream& in, uint8_t flags)
in_place_reduce(); 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) const string& smaller_str)
{ {
amount_t larger, smaller; amount_t larger, smaller;
larger.parse(larger_str, AMOUNT_PARSE_NO_REDUCE); larger.parse(parent, larger_str, AMOUNT_PARSE_NO_REDUCE);
smaller.parse(smaller_str, AMOUNT_PARSE_NO_REDUCE); smaller.parse(parent, smaller_str, AMOUNT_PARSE_NO_REDUCE);
larger *= smaller.number(); larger *= smaller.number();
@ -1323,7 +1271,7 @@ void amount_t::print(std::ostream& _out, bool omit_commodity,
if (! omit_commodity && comm.annotated) { if (! omit_commodity && comm.annotated) {
annotated_commodity_t& ann(static_cast<annotated_commodity_t&>(comm)); annotated_commodity_t& ann(static_cast<annotated_commodity_t&>(comm));
assert(&*ann.price != this); assert(&*ann.details.price != this);
ann.write_annotations(out); 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; commodity_t::ident_t ident;
read_binary_long(in, ident); read_binary_long(in, ident);
if (ident == 0xffffffff) if (ident == 0xffffffff)
commodity_ = NULL; commodity_ = NULL;
else if (ident == 0) else if (ident == 0)
commodity_ = commodity_t::null_commodity; commodity_ = parent.null_commodity;
else else {
commodity_ = (*commodity_t::commodities_by_ident)[ident - 1]; commodity_ = parent.find(ident - 1);
assert(commodity_);
}
read_quantity(in); read_quantity(in);
} }
void amount_t::read(char *& data) void amount_t::read(commodity_pool_t& parent, char *& data)
{ {
commodity_t::ident_t ident; commodity_t::ident_t ident;
read_binary_long(data, ident); read_binary_long(data, ident);
if (ident == 0xffffffff) if (ident == 0xffffffff)
commodity_ = NULL; commodity_ = NULL;
else if (ident == 0) else if (ident == 0)
commodity_ = commodity_t::null_commodity; commodity_ = parent.null_commodity;
else else {
commodity_ = (*commodity_t::commodities_by_ident)[ident - 1]; commodity_ = parent.find(ident - 1);
assert(commodity_);
}
read_quantity(data); read_quantity(data);
} }

View file

@ -52,6 +52,8 @@
namespace ledger { namespace ledger {
class commodity_t; class commodity_t;
class annotation_t;
class commodity_pool_t;
DECLARE_EXCEPTION(amount_error); DECLARE_EXCEPTION(amount_error);
@ -87,6 +89,12 @@ public:
static void initialize(); static void initialize();
static void shutdown(); 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 * The `keep_base' member determines whether scalable commodities
* are automatically converted to their most reduced form when * are automatically converted to their most reduced form when
@ -291,11 +299,12 @@ public:
* compact form greater than 1.0. That is, 3599s will unreduce to * compact form greater than 1.0. That is, 3599s will unreduce to
* 59.98m, while 3601 unreduces to 1h. * 59.98m, while 3601 unreduces to 1h.
* *
* value(moment_t) returns the history value of an amount, based on * value(optional<moment_t>) returns the historical value for an
* the price history of its commodity. For example, if the amount * amount -- the default moment returns the most recently known
* were 10 AAPL, and on Apr 10, 2000 each share of AAPL was worth * price -- based on the price history of its commodity. For
* $10, then call value() for that moment in time would yield the * example, if the amount were 10 AAPL, and on Apr 10, 2000 each
* amount $100.00. * 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 * Further, for the sake of efficiency and avoiding temporary
* objects, the following methods support "in-place" variants that * objects, the following methods support "in-place" variants that
@ -341,7 +350,8 @@ public:
} }
amount_t& in_place_unreduce(); 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: * 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 * the price argument is required, although it can be passed as
* `optional<amount_t>()' if no price is desired. * `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 * strip_annotations([keep_price, keep_date, keep_tag]) returns an
* amount whose commodity's annotations have been stripped. The * amount whose commodity's annotations have been stripped. The
* three `keep_' arguments determine which annotation detailed are * three `keep_' arguments determine which annotation detailed are
* kept, meaning that the default is to follow whatever * kept, meaning that the default is to follow whatever
* amount_t::keep_price, amount_t::keep_date and amount_t::keep_tag * amount_t::keep_price, amount_t::keep_date and amount_t::keep_tag
* have been set to (which all default to false). * 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, void annotate_commodity(const annotation_t& details);
const optional<moment_t>& tdate = optional<moment_t>(), bool commodity_annotated() const;
const optional<string>& ttag = optional<string>()); annotation_t annotation_details() const;
amount_t strip_annotations(const bool _keep_price = keep_price,
amount_t strip_annotations(const bool _keep_price = keep_price, const bool _keep_date = keep_date,
const bool _keep_date = keep_date, const bool _keep_tag = keep_tag) const;
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
/** /**
* Parsing methods. The method `parse' is used to parse an amount * 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 * defined which simply calls parse on the input stream. The
* `parse' method has two forms: * `parse' method has two forms:
* *
* parse(istream, unsigned char flags) parses an amount from the * parse(commodity_pool_t, istream, flags_t) parses an
* given input stream. * amount from the given input stream, registering commodity details
* according to the commodity pool which is passed in as the first
* parameter.
* *
* parse(string, unsigned char flags) parses an amount from the * parse(istream, flags_t) is the same as the preceding function,
* given string. * only it uses `amount_t::default_pool' as the commodity pool.
*
* 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 * The `flags' argument of both parsing may be one or more of the
* following: * following:
@ -538,15 +543,36 @@ public:
* amount_t::parse_conversion("1.0m", "60s"); // a minute is 60 seconds * amount_t::parse_conversion("1.0m", "60s"); // a minute is 60 seconds
* amount_t::parse_conversion("1.0h", "60m"); // an hour is 60 minutes * amount_t::parse_conversion("1.0h", "60m"); // an hour is 60 minutes
*/ */
void parse(std::istream& in, unsigned char flags = 0); #define AMOUNT_PARSE_NO_MIGRATE 0x01
void parse(const string& str, unsigned char flags = 0) { #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); 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); 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 * Printing methods. An amount may be output to a stream using the
* `print' method. There is also a global operator<< defined which * `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 * input stream or a character pointer, and it may be serialized to
* an output stream. The methods used are: * an output stream. The methods used are:
* *
* read(istream) reads an amount from the given input stream. It * read(commodity_pool_t, istream) reads an amount from the given
* must have been put there using `write(ostream)'. * 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(char *&) reads an amount from data which has been read from * read(istream) does the same as read, only it relies on
* an input stream into a buffer. it advances the pointer passed in * `amount_t::default_pool' to specify the pool.
* to the end of the deserialized amount. *
* 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 *&) 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 * write(ostream) writes an amount to an output stream in a compact
* binary format. * binary format.
*/ */
void read(std::istream& in); void read(commodity_pool_t& parent, std::istream& in);
void read(char *& data); 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; void write(std::ostream& out) const;
@ -614,10 +662,9 @@ public:
bool valid() const; bool valid() const;
private: private:
friend void parse_annotations(std::istream& in, friend bool parse_annotations(commodity_pool_t& parent,
optional<amount_t>& price, std::istream& in,
optional<moment_t>& date, annotation_t& details);
optional<string>& tag);
}; };
inline amount_t amount_t::exact(const string& value) { 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 { 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 { 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 } // namespace ledger

View file

@ -133,7 +133,8 @@ balance_t& balance_t::operator/=(const amount_t& amt)
return *this; 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 (! commodity) {
if (amounts.size() == 1) { if (amounts.size() == 1) {
@ -151,71 +152,27 @@ amount_t balance_t::amount(const commodity_t& commodity) const
} }
} }
else if (amounts.size() > 0) { 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()) if (i != amounts.end())
return (*i).second; return (*i).second;
} }
return amount_t(); return optional<amount_t>();
} }
balance_t balance_t::value(const moment_t& moment) const optional<balance_t>
{ balance_t::value(const optional<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> temp; optional<balance_t> temp;
for (amounts_map::const_iterator i = amounts.begin(); for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end(); i != amounts.end();
i++) { i++)
optional<amount_t> i_price = (*i).second.price(); if (optional<amount_t> val = (*i).second.value(moment)) {
if (i_price) {
if (! temp) if (! temp)
temp = balance_t(); 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; return temp;
} }
@ -243,52 +200,33 @@ void balance_t::write(std::ostream& out,
if (lwidth == -1) if (lwidth == -1)
lwidth = first_width; lwidth = first_width;
if (commodity_t::commodities_sorted) { typedef std::vector<const amount_t *> amounts_array;
for (amounts_map::const_iterator i = amounts.begin(); amounts_array sorted;
i != amounts.end();
i++) {
int width;
if (! first) {
out << std::endl;
width = lwidth;
} else {
first = false;
width = first_width;
}
out.width(width); for (amounts_map::const_iterator i = amounts.begin();
out.fill(' '); i != amounts.end();
out << std::right << (*i).second; i++)
if ((*i).second)
sorted.push_back(&(*i).second);
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;
} }
} else {
typedef std::vector<const amount_t *> amounts_array;
amounts_array sorted;
for (amounts_map::const_iterator i = amounts.begin(); out.width(width);
i != amounts.end(); out.fill(' ');
i++) out << std::right << **i;
if ((*i).second)
sorted.push_back(&(*i).second);
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;
}
} }
if (first) { if (first) {

View file

@ -93,15 +93,19 @@ public:
bool operator<(const balance_t& bal) const { bool operator<(const balance_t& bal) const {
for (amounts_map::const_iterator i = bal.amounts.begin(); for (amounts_map::const_iterator i = bal.amounts.begin();
i != bal.amounts.end(); i != bal.amounts.end();
i++) i++) {
if (! (amount(*(*i).first) < (*i).second)) optional<amount_t> amt = amount(*(*i).first);
if (amt && ! (*amt < (*i).second))
return false; return false;
}
for (amounts_map::const_iterator i = amounts.begin(); for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end(); i != amounts.end();
i++) i++) {
if (! ((*i).second < bal.amount(*(*i).first))) optional<amount_t> amt = bal.amount(*(*i).first);
if (amt && ! ((*i).second < *amt))
return false; return false;
}
if (bal.amounts.size() == 0 && amounts.size() == 0) if (bal.amounts.size() == 0 && amounts.size() == 0)
return false; return false;
@ -146,13 +150,10 @@ public:
return true; return true;
} }
amount_t amount(const commodity_t& commodity = optional<amount_t> amount(const optional<const commodity_t&>& commodity =
*commodity_t::null_commodity) const; optional<const commodity_t&>()) const;
balance_t value(const moment_t& moment = now) const; optional<balance_t> value(const optional<moment_t>& moment =
optional<moment_t>()) const;
optional<balance_t> price() const;
optional<moment_t> date() const;
optional<string> tag() const;
balance_t balance_t
strip_annotations(const bool keep_price = amount_t::keep_price, strip_annotations(const bool keep_price = amount_t::keep_price,
@ -335,24 +336,15 @@ public:
return temp; return temp;
} }
amount_t amount(const commodity_t& commodity = optional<amount_t> amount(const optional<const commodity_t&>& commodity =
*commodity_t::null_commodity) const { optional<const commodity_t&>()) const {
return quantity.amount(commodity); 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); 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 balance_t
strip_annotations(const bool keep_price = amount_t::keep_price, strip_annotations(const bool keep_price = amount_t::keep_price,
const bool keep_date = amount_t::keep_date, const bool keep_date = amount_t::keep_date,

View file

@ -43,61 +43,98 @@
namespace ledger { namespace ledger {
#ifndef THREADSAFE void commodity_t::add_price(const moment_t& date,
base_commodities_map commodity_base_t::commodities; const amount_t& price)
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)
{ {
if (! history) if (! base->history)
history = history_t(); base->history = history_t();
history_map::iterator i = history->prices.find(date); history_map::iterator i = base->history->prices.find(date);
if (i != history->prices.end()) { if (i != base->history->prices.end()) {
(*i).second = price; (*i).second = price;
} else { } else {
std::pair<history_map::iterator, bool> result 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); assert(result.second);
} }
} }
bool commodity_base_t::remove_price(const moment_t& date) bool commodity_t::remove_price(const moment_t& date)
{ {
if (history) { if (base->history) {
history_map::size_type n = history->prices.erase(date); history_map::size_type n = base->history->prices.erase(date);
if (n > 0) { if (n > 0) {
if (history->prices.empty()) if (base->history->prices.empty())
history.reset(); base->history.reset();
return true; return true;
} }
} }
return false; 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);
std::pair<base_commodities_map::iterator, bool> result if (! moment) {
= commodities.insert(base_commodities_pair(symbol, commodity)); history_map::reverse_iterator r = base->history->prices.rbegin();
assert(result.second); 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;
}
}
}
}
return commodity; 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;
} }
bool commodity_t::needs_quotes(const string& symbol) commodity_t::operator bool() const
{
return this != parent().null_commodity;
}
annotated_commodity_t& commodity_t::as_annotated()
{
assert(annotated);
return *polymorphic_downcast<annotated_commodity_t *>(this);
}
const annotated_commodity_t& commodity_t::as_annotated() const
{
assert(annotated);
return *polymorphic_downcast<const annotated_commodity_t *>(this);
}
bool commodity_t::symbol_needs_quotes(const string& symbol)
{ {
for (const char * p = symbol.c_str(); *p; p++) for (const char * p = symbol.c_str(); *p; p++)
if (std::isspace(*p) || std::isdigit(*p) || *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 bool commodity_t::valid() const
{ {
if (symbol().empty() && this != null_commodity) { if (symbol().empty() && this != parent().null_commodity) {
DEBUG("ledger.validate", DEBUG("ledger.validate",
"commodity_t: symbol().empty() && this != null_commodity"); "commodity_t: symbol().empty() && this != null_commodity");
return false; return false;
@ -127,224 +164,34 @@ bool commodity_t::valid() const
return true; return true;
} }
commodity_t * commodity_t::create(const string& symbol)
{
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;
commodity->ident = commodities_by_ident->size();
commodities_by_ident->push_back(commodity.get());
// 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);
return commodity.release();
}
commodity_t * commodity_t::find_or_create(const string& symbol)
{
DEBUG("amounts.commodities", "Find-or-create commodity " << symbol);
commodity_t * commodity = find(symbol);
if (commodity)
return commodity;
return create(symbol);
}
commodity_t * commodity_t::find(const string& symbol)
{
DEBUG("amounts.commodities", "Find commodity " << symbol);
commodities_map::const_iterator i = commodities.find(symbol);
if (i != commodities.end())
return (*i).second;
return NULL;
}
amount_t commodity_base_t::value(const moment_t& moment)
{
moment_t age;
amount_t price;
if (history) {
assert(history->prices.size() > 0);
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;
} 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;
}
}
}
}
if (updater && ! (flags & COMMODITY_STYLE_NOMARKET))
(*updater)(*this, moment, age,
(history && history->prices.size() > 0 ?
(*history->prices.rbegin()).first : moment_t()), price);
return price;
}
bool annotated_commodity_t::operator==(const commodity_t& comm) const bool annotated_commodity_t::operator==(const commodity_t& comm) const
{ {
// If the base commodities don't match, the game's up. // If the base commodities don't match, the game's up.
if (base != comm.base) if (base != comm.base)
return false; return false;
if (price && assert(annotated);
(! comm.annotated || if (! comm.annotated)
price != static_cast<const annotated_commodity_t&>(comm).price))
return false; return false;
if (date && if (details != comm.as_annotated().details)
(! comm.annotated ||
date != static_cast<const annotated_commodity_t&>(comm).date))
return false;
if (tag &&
(! comm.annotated ||
tag != static_cast<const annotated_commodity_t&>(comm).tag))
return false; return false;
return true; return true;
} }
void void
annotated_commodity_t::write_annotations(std::ostream& out, annotated_commodity_t::write_annotations(std::ostream& out,
const optional<amount_t>& price, const annotation_t& info)
const optional<moment_t>& date,
const optional<string>& tag)
{ {
if (price) if (info.price)
out << " {" << *price << '}'; out << " {" << *info.price << '}';
if (date) if (info.date)
out << " [" << *date << ']'; out << " [" << *info.date << ']';
if (tag) if (info.tag)
out << " (" << *tag << ')'; out << " (" << *info.tag << ')';
}
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)
{
std::auto_ptr<annotated_commodity_t> commodity(new annotated_commodity_t);
// Set the annotated bits
commodity->price = price;
commodity->date = date;
commodity->tag = tag;
commodity->ptr = &comm;
assert(commodity->ptr);
commodity->base = comm.base;
assert(commodity->base);
commodity->qualified_symbol = comm.symbol();
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"));
// 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)
return NULL;
commodity->ident = commodities_by_ident->size();
commodities_by_ident->push_back(commodity.get());
return commodity.release();
}
namespace {
string make_qualified_name(const commodity_t& comm,
const optional<amount_t>& price,
const optional<moment_t>& date,
const optional<string>& tag)
{
if (price && 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);
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"));
DEBUG("amounts.commodities", "qualified_name is " << name.str());
return name.str();
}
}
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)
{
string name = make_qualified_name(comm, price, date, tag);
commodity_t * ann_comm = commodity_t::find(name);
if (ann_comm) {
assert(ann_comm->annotated);
return ann_comm;
}
return create(comm, price, date, tag, name);
} }
bool compare_amount_commodities::operator()(const amount_t * left, bool compare_amount_commodities::operator()(const amount_t * left,
@ -369,15 +216,15 @@ bool compare_amount_commodities::operator()(const amount_t * left,
annotated_commodity_t& aleftcomm(static_cast<annotated_commodity_t&>(leftcomm)); annotated_commodity_t& aleftcomm(static_cast<annotated_commodity_t&>(leftcomm));
annotated_commodity_t& arightcomm(static_cast<annotated_commodity_t&>(rightcomm)); annotated_commodity_t& arightcomm(static_cast<annotated_commodity_t&>(rightcomm));
if (! aleftcomm.price && arightcomm.price) if (! aleftcomm.details.price && arightcomm.details.price)
return true; return true;
if (aleftcomm.price && ! arightcomm.price) if (aleftcomm.details.price && ! arightcomm.details.price)
return false; return false;
if (aleftcomm.price && arightcomm.price) { if (aleftcomm.details.price && arightcomm.details.price) {
amount_t leftprice(*aleftcomm.price); amount_t leftprice(*aleftcomm.details.price);
leftprice.in_place_reduce(); leftprice.in_place_reduce();
amount_t rightprice(*arightcomm.price); amount_t rightprice(*arightcomm.details.price);
rightprice.in_place_reduce(); rightprice.in_place_reduce();
if (leftprice.commodity() == rightprice.commodity()) { if (leftprice.commodity() == rightprice.commodity()) {
@ -392,27 +239,239 @@ bool compare_amount_commodities::operator()(const amount_t * left,
} }
} }
if (! aleftcomm.date && arightcomm.date) if (! aleftcomm.details.date && arightcomm.details.date)
return true; return true;
if (aleftcomm.date && ! arightcomm.date) if (aleftcomm.details.date && ! arightcomm.details.date)
return false; return false;
if (aleftcomm.date && arightcomm.date) { if (aleftcomm.details.date && arightcomm.details.date) {
duration_t diff = *aleftcomm.date - *arightcomm.date; duration_t diff = *aleftcomm.details.date - *arightcomm.details.date;
return diff.is_negative(); return diff.is_negative();
} }
if (! aleftcomm.tag && arightcomm.tag) if (! aleftcomm.details.tag && arightcomm.details.tag)
return true; return true;
if (aleftcomm.tag && ! arightcomm.tag) if (aleftcomm.details.tag && ! arightcomm.details.tag)
return false; return false;
if (aleftcomm.tag && arightcomm.tag) if (aleftcomm.details.tag && arightcomm.details.tag)
return *aleftcomm.tag < *arightcomm.tag; return *aleftcomm.details.tag < *arightcomm.details.tag;
assert(false); assert(false);
return true; return true;
} }
} }
commodity_pool_t::commodity_pool_t() : default_commodity(NULL)
{
null_commodity = create("");
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 = 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");
}
commodity_t * commodity_pool_t::create(const string& symbol)
{
shared_ptr<commodity_base_t> base_commodity(new commodity_base_t(symbol));
std::auto_ptr<commodity_t> commodity(new commodity_t(this, base_commodity));
DEBUG("amounts.commodities", "Creating base commodity " << symbol);
// 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 += "\"";
}
DEBUG("amounts.commodities",
"Creating commodity '" << commodity->symbol() << "'");
// 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();
}
}
commodity_t * commodity_pool_t::find_or_create(const string& symbol)
{
DEBUG("amounts.commodities", "Find-or-create commodity " << symbol);
commodity_t * commodity = find(symbol);
if (commodity)
return commodity;
return create(symbol);
}
commodity_t * commodity_pool_t::find(const string& symbol)
{
DEBUG("amounts.commodities", "Find commodity " << symbol);
typedef commodity_pool_t::commodities_t::nth_index<1>::type
commodities_by_name;
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_t * commodity_pool_t::find(const commodity_t::ident_t ident)
{
DEBUG("amounts.commodities", "Find commodity by ident " << ident);
typedef commodity_pool_t::commodities_t::nth_index<0>::type
commodities_by_ident;
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_t *
commodity_pool_t::create(const string& symbol, const annotation_t& details)
{
commodity_t * new_comm = create(symbol);
if (! new_comm)
return NULL;
if (details)
return find_or_create(*new_comm, details);
else
return new_comm;
}
namespace {
string make_qualified_name(const commodity_t& comm,
const annotation_t& details)
{
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, details);
DEBUG("amounts.commodities", "make_qualified_name for "
<< comm.qualified_symbol << std::endl << details);
DEBUG("amounts.commodities", "qualified_name is " << name.str());
return name.str();
}
}
commodity_t *
commodity_pool_t::find(const string& symbol, const annotation_t& details)
{
commodity_t * comm = find(symbol);
if (! comm)
return NULL;
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;
}
}
commodity_t *
commodity_pool_t::find_or_create(const string& symbol,
const annotation_t& details)
{
commodity_t * comm = find(symbol);
if (! comm)
return NULL;
if (details)
return find_or_create(*comm, details);
else
return comm;
}
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());
std::auto_ptr<commodity_t> commodity
(new annotated_commodity_t(&comm, details));
commodity->qualified_symbol = comm.symbol();
assert(! commodity->qualified_symbol->empty());
DEBUG("amounts.commodities", "Creating annotated commodity "
<< "symbol " << commodity->symbol()
<< " key " << mapping_key << std::endl << details);
// 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;
std::pair<commodities_t::iterator, bool> result
= commodities.insert(commodity.get());
if (! result.second) {
assert(false);
return NULL;
} else {
return commodity.release();
}
}
commodity_t * commodity_pool_t::find_or_create(commodity_t& comm,
const annotation_t& details)
{
assert(comm);
assert(details);
string name = make_qualified_name(comm, details);
assert(! name.empty());
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 } // namespace ledger

View file

@ -53,232 +53,229 @@ namespace ledger {
#define COMMODITY_STYLE_NOMARKET 0x0010 #define COMMODITY_STYLE_NOMARKET 0x0010
#define COMMODITY_STYLE_BUILTIN 0x0020 #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 class commodity_base_t : public noncopyable
{ {
public: private:
friend class commodity_pool_t;
friend class commodity_t; friend class commodity_t;
friend class annotated_commodity_t; friend class annotated_commodity_t;
friend void amount_t::initialize(); typedef std::map<const moment_t, amount_t> history_map;
friend void amount_t::shutdown(); typedef std::pair<const moment_t, amount_t> history_pair;
friend void checked_delete<commodity_base_t>(commodity_base_t *);
typedef uint_least32_t ident_t;
ident_t ident;
string name;
string note;
amount_t::precision_t precision;
unsigned char flags;
optional<amount_t> smaller;
optional<amount_t> larger;
commodity_base_t() : precision(0), flags(COMMODITY_STYLE_DEFAULTS) {
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");
}
~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 { struct history_t {
history_map prices; history_map prices;
ptime last_lookup; ptime last_lookup;
history_t() : last_lookup() {}
}; };
optional<history_t> history;
void add_price(const moment_t& date, const amount_t& price); typedef uint_least8_t flags_t;
bool remove_price(const moment_t& date);
amount_t value(const moment_t& moment = now); flags_t flags;
string symbol;
amount_t::precision_t precision;
optional<string> name;
optional<string> note;
optional<history_t> history;
optional<amount_t> smaller;
optional<amount_t> larger;
public: public:
class updater_t { explicit commodity_base_t()
public: : flags(COMMODITY_STYLE_DEFAULTS), precision(0) {
virtual ~updater_t() {} TRACE_CTOR(commodity_base_t, "");
virtual void operator()(commodity_base_t& commodity, }
const moment_t& moment, explicit commodity_base_t
const moment_t& date, (const string& _symbol,
const moment_t& last, amount_t::precision_t _precision = 0,
amount_t& price) = 0; unsigned int _flags = COMMODITY_STYLE_DEFAULTS)
}; : flags(_flags), symbol(_symbol), precision(_precision) {
friend class updater_t; TRACE_CTOR(commodity_base_t,
"const string&, amount_t::precision_t, unsigned int");
static updater_t * updater; }
~commodity_base_t() {
TRACE_DTOR(commodity_base_t);
}
}; };
typedef std::map<const string, commodity_t *> commodities_map; class annotated_commodity_t;
typedef std::pair<const string, commodity_t *> commodities_pair;
typedef std::vector<commodity_t *> commodities_array;
class commodity_t class commodity_t
: public equality_comparable<commodity_t, noncopyable> : public equality_comparable1<commodity_t, noncopyable>
{ {
friend class annotated_commodity_t;
public: public:
// This map remembers all commodities that have been defined. static bool symbol_needs_quotes(const string& symbol);
static commodities_map commodities; typedef commodity_base_t::flags_t flags_t;
static commodities_array * commodities_by_ident; typedef commodity_base_t::history_t history_t;
static bool commodities_sorted; typedef commodity_base_t::history_map history_map;
static commodity_t * null_commodity; typedef commodity_base_t::history_pair history_pair;
static commodity_t * default_commodity; typedef uint_least32_t ident_t;
static commodity_t * create(const string& symbol); shared_ptr<commodity_base_t> base;
static commodity_t * find(const string& name);
static commodity_t * find_or_create(const string& symbol);
static bool needs_quotes(const string& symbol);
static void make_alias(const string& symbol,
commodity_t * commodity);
// These are specific to each commodity reference
typedef unsigned long ident_t;
commodity_pool_t * parent_;
ident_t ident; ident_t ident;
commodity_base_t * base; optional<string> qualified_symbol;
string qualified_symbol; optional<string> mapping_key_;
bool annotated; bool annotated;
public: 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, ""); 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() { virtual ~commodity_t() {
TRACE_DTOR(commodity_t); TRACE_DTOR(commodity_t);
} }
operator bool() const { operator bool() const;
return this != null_commodity;
}
virtual bool operator==(const commodity_t& comm) const { virtual bool operator==(const commodity_t& comm) const {
if (comm.annotated) if (comm.annotated)
return comm == *this; 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 { string base_symbol() const {
return base->symbol; return base->symbol;
} }
string symbol() const { string symbol() const {
return qualified_symbol; return qualified_symbol ? *qualified_symbol : base_symbol();
} }
void write(std::ostream& out) const { string mapping_key() const {
out << symbol(); if (mapping_key_)
return *mapping_key_;
else
return base_symbol();
} }
string name() const { optional<string> name() const {
return base->name; return base->name;
} }
void set_name(const string& arg) { void set_name(const optional<string>& arg = optional<string>()) {
base->name = arg; base->name = arg;
} }
string note() const { optional<string> note() const {
return base->note; return base->note;
} }
void set_note(const string& arg) { void set_note(const optional<string>& arg = optional<string>()) {
base->note = arg; base->note = arg;
} }
unsigned char precision() const { amount_t::precision_t precision() const {
return base->precision; return base->precision;
} }
void set_precision(unsigned char arg) { void set_precision(amount_t::precision_t arg) {
base->precision = arg; base->precision = arg;
} }
unsigned char flags() const { flags_t flags() const {
return base->flags; return base->flags;
} }
void set_flags(unsigned char arg) { void set_flags(flags_t arg) {
base->flags = arg; base->flags = arg;
} }
void add_flags(unsigned char arg) { void add_flags(flags_t arg) {
base->flags |= arg; base->flags |= arg;
} }
void drop_flags(unsigned char arg) { void drop_flags(flags_t arg) {
base->flags &= ~arg; base->flags &= ~arg;
} }
optional<amount_t> smaller() const { optional<amount_t> smaller() const {
return base->smaller; return base->smaller;
} }
void set_smaller(const amount_t& arg) { void set_smaller(const optional<amount_t>& arg = optional<amount_t>()) {
base->smaller = arg; base->smaller = arg;
} }
optional<amount_t> larger() const { optional<amount_t> larger() const {
return base->larger; return base->larger;
} }
void set_larger(const amount_t& arg) { void set_larger(const optional<amount_t>& arg = optional<amount_t>()) {
base->larger = arg; base->larger = arg;
} }
optional<commodity_base_t::history_t> history() const { optional<history_t> history() const {
return base->history; return base->history;
} }
void add_price(const moment_t& date, const amount_t& price) { void add_price(const moment_t& date, const amount_t& price);
return base->add_price(date, price); bool remove_price(const moment_t& date);
}
bool remove_price(const moment_t& date) { optional<amount_t> value(const optional<moment_t>& moment =
return base->remove_price(date); optional<moment_t>());
}
amount_t value(const moment_t& moment = now) const { void write(std::ostream& out) const {
return base->value(moment); out << symbol();
} }
bool valid() const; 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: optional<amount_t> price;
const commodity_t * ptr; optional<moment_t> date;
optional<string> tag;
optional<amount_t> price; explicit annotation_t(const optional<amount_t>& _price = optional<amount_t>(),
optional<moment_t> date; const optional<moment_t>& _date = optional<moment_t>(),
optional<string> tag; 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, ""); TRACE_CTOR(annotated_commodity_t, "");
annotated = true; annotated = true;
} }
@ -288,39 +285,131 @@ class annotated_commodity_t : public commodity_t
virtual bool operator==(const commodity_t& comm) const; virtual bool operator==(const commodity_t& comm) const;
void write_annotations(std::ostream& out) const { commodity_t& referent() {
annotated_commodity_t::write_annotations(out, price, date, tag); return *ptr;
}
const commodity_t& referent() const {
return *ptr;
} }
static void write_annotations(std::ostream& out, void write_annotations(std::ostream& out) const {
const optional<amount_t>& price, annotated_commodity_t::write_annotations(out, details);
const optional<moment_t>& date, }
const optional<string>& tag);
private: static void write_annotations(std::ostream& out,
static commodity_t * create(const commodity_t& comm, const annotation_t& info);
const optional<amount_t>& price,
const optional<moment_t>& date,
const optional<string>& tag,
const string& mapping_key);
static commodity_t * find_or_create(const commodity_t& comm,
const optional<amount_t>& price,
const optional<moment_t>& date,
const optional<string>& tag);
friend class amount_t;
}; };
inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) {
out << comm.symbol();
return out;
}
struct compare_amount_commodities { struct compare_amount_commodities {
bool operator()(const amount_t * left, const amount_t * right) const; 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);
}
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);
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);
commodity_t * create(commodity_t& comm,
const annotation_t& details,
const string& mapping_key);
commodity_t * find_or_create(commodity_t& comm,
const annotation_t& details);
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);
}
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 } // namespace ledger
#endif // _COMMODITY_H #endif // _COMMODITY_H

View file

@ -191,7 +191,7 @@ void dataHandler(void *userData, const char *s, int len)
string symbol(s, len); string symbol(s, len);
if (symbol == "USD") symbol = "$"; 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); assert(parser->curr_comm);
if (symbol != "$") 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 // GnuCash uses the USD commodity without defining it, which really
// means $. // means $.
commodity_t * usd = commodity_t::find_or_create("$"); commodity_t * usd = amount_t::default_pool->find_or_create("$");
usd->set_precision(2); usd->set_precision(2);
usd->add_flags(COMMODITY_STYLE_THOUSANDS); usd->add_flags(COMMODITY_STYLE_THOUSANDS);

View file

@ -115,8 +115,9 @@ bool entry_base_t::finalize()
annotated_commodity_t& annotated_commodity_t&
ann_comm(static_cast<annotated_commodity_t&> ann_comm(static_cast<annotated_commodity_t&>
((*x)->amount->commodity())); ((*x)->amount->commodity()));
if (ann_comm.price) if (ann_comm.details.price)
balance += *ann_comm.price * (*x)->amount->number() - *((*x)->cost); balance += (*ann_comm.details.price * (*x)->amount->number() -
*((*x)->cost));
} }
} else { } else {
saw_null = true; saw_null = true;
@ -173,9 +174,9 @@ bool entry_base_t::finalize()
if ((*x)->amount->commodity() && if ((*x)->amount->commodity() &&
! (*x)->amount->commodity().annotated) ! (*x)->amount->commodity().annotated)
(*x)->amount->annotate_commodity (*x)->amount->annotate_commodity
(per_unit_cost.abs(), (annotation_t(per_unit_cost.abs(),
entry ? entry->actual_date() : optional<moment_t>(), entry ? entry->actual_date() : optional<moment_t>(),
entry ? entry->code : optional<string>()); entry ? entry->code : optional<string>()));
(*x)->cost = - (per_unit_cost * (*x)->amount->number()); (*x)->cost = - (per_unit_cost * (*x)->amount->number());
balance += *(*x)->cost; balance += *(*x)->cost;
@ -610,14 +611,6 @@ bool journal_t::valid() const
return false; 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; return true;
} }

View file

@ -408,26 +408,26 @@ int main(int argc, char * argv[], char * envp[])
#if defined(TRACING_ON) #if defined(TRACING_ON)
if (i + 1 < argc && std::strcmp(argv[i], "--trace") == 0) { if (i + 1 < argc && std::strcmp(argv[i], "--trace") == 0) {
ledger::_log_level = LOG_TRACE; ledger::_log_level = LOG_TRACE;
ledger::_trace_level = lexical_cast<int>(argv[i + 1]); ledger::_trace_level = std::atoi(argv[i + 1]);
i++; i++;
} }
#endif #endif
} }
IF_VERIFY()
initialize_memory_tracing();
try { try {
std::ios::sync_with_stdio(false); std::ios::sync_with_stdio(false);
boost::filesystem::path::default_name_check boost::filesystem::path::default_name_check
(boost::filesystem::portable_posix_name); (boost::filesystem::portable_posix_name);
ledger::initialize();
#if ! defined(FULL_DEBUG)
ledger::do_cleanup = false;
#endif
INFO("Ledger starting"); INFO("Ledger starting");
std::auto_ptr<ledger::session_t> session(new ledger::session_t); std::auto_ptr<ledger::session_t> session(new ledger::session_t);
ledger::set_session_context(session.get());
#if 0 #if 0
session->register_parser(new binary_parser_t); session->register_parser(new binary_parser_t);
#endif #endif
@ -445,9 +445,11 @@ int main(int argc, char * argv[], char * envp[])
status = read_and_report(report.get(), argc, argv, envp); status = read_and_report(report.get(), argc, argv, envp);
IF_VERIFY() { if (! DO_VERIFY()) {
report.release(); report.release();
session.release(); session.release();
} else {
ledger::set_session_context();
} }
} }
#if 0 #if 0
@ -480,8 +482,12 @@ int main(int argc, char * argv[], char * envp[])
status = _status; status = _status;
} }
IF_VERIFY() IF_VERIFY() {
ledger::shutdown(); INFO("Ledger ended (Boost/libstdc++ may still hold memory)");
shutdown_memory_tracing();
} else {
INFO("Ledger ended");
}
return status; return status;
} }

View file

@ -110,7 +110,7 @@ unsigned int qif_parser_t::parse(std::istream& in,
unsigned char prec = xact->amount->commodity().precision(); unsigned char prec = xact->amount->commodity().precision();
if (! def_commodity) { if (! def_commodity) {
def_commodity = commodity_t::find_or_create("$"); def_commodity = amount_t::default_pool->find_or_create("$");
assert(def_commodity); assert(def_commodity);
} }
xact->amount->set_commodity(*def_commodity); xact->amount->set_commodity(*def_commodity);

View file

@ -2,31 +2,36 @@
namespace ledger { namespace ledger {
void quotes_by_script::operator()(commodity_base_t& commodity, optional<amount_t>
const ptime& moment, quotes_by_script::operator()(commodity_t& commodity,
const ptime& date, const optional<moment_t>& date,
const ptime& last, const optional<moment_t>& moment,
amount_t& price) const optional<moment_t>& last)
{ {
LOGGER("quotes.download"); LOGGER("quotes.download");
DEBUG_("commodity: " << commodity.symbol); IF_DEBUG_() {
DEBUG_(" now: " << now); DEBUG_("commodity: " << commodity.symbol());
DEBUG_(" moment: " << moment); DEBUG_(" now: " << now);
DEBUG_(" date: " << date); if (date)
DEBUG_(" last: " << last); DEBUG_(" date: " << date);
if (moment)
if (SHOW_DEBUG_() && commodity.history) DEBUG_(" moment: " << moment);
DEBUG_("last_lookup: " << commodity.history->last_lookup); if (last)
DEBUG_(" last: " << last);
if (commodity.history())
DEBUG_("last_lookup: " << commodity.history()->last_lookup);
}
DEBUG_("pricing_leeway is " << pricing_leeway); DEBUG_("pricing_leeway is " << pricing_leeway);
if ((commodity.history && if ((commodity.history() &&
(time_now - commodity.history->last_lookup) < pricing_leeway) || (now - commodity.history()->last_lookup) < pricing_leeway) ||
(time_now - last) < pricing_leeway || (last && (now - *last) < pricing_leeway) ||
(price && moment > date && (moment - date) <= pricing_leeway)) (moment && date && *moment > *date &&
return; (*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]; char buf[256];
buf[0] = '\0'; buf[0] = '\0';
@ -34,7 +39,7 @@ void quotes_by_script::operator()(commodity_base_t& commodity,
bool success = true; bool success = true;
if (FILE * fp = popen((string("getquote \"") + if (FILE * fp = popen((string("getquote \"") +
commodity.symbol + "\"").c_str(), "r")) { commodity.base_symbol() + "\"").c_str(), "r")) {
if (feof(fp) || ! fgets(buf, 255, fp)) if (feof(fp) || ! fgets(buf, 255, fp))
success = false; success = false;
if (pclose(fp) != 0) if (pclose(fp) != 0)
@ -49,31 +54,31 @@ void quotes_by_script::operator()(commodity_base_t& commodity,
DEBUG_("downloaded quote: " << buf); DEBUG_("downloaded quote: " << buf);
amount_t price;
price.parse(buf); price.parse(buf);
commodity.add_price(now, price); commodity.add_price(now, price);
commodity.history->last_lookup = time_now; commodity.history()->last_lookup = now;
cache_dirty = true; cache_dirty = true;
if (price) { assert(! price_db.empty());
assert(! price_db.empty());
#if defined(__GNUG__) && __GNUG__ < 3 #if defined(__GNUG__) && __GNUG__ < 3
ofstream database(price_db, ios::out | ios::app); ofstream database(price_db, ios::out | ios::app);
#else #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 #endif
#if 0 #if 0
// jww (2007-04-18): Need to convert to local time and print // jww (2007-04-18): Need to convert to local time and print
// here, print with UTC timezone specifier // here, print with UTC timezone specifier
database << "P " << now.to_string("%Y/%m/%d %H:%M:%S") database << "P " << now.to_string("%Y/%m/%d %H:%M:%S")
<< " " << commodity.symbol << " " << price << endl; << " " << commodity.symbol << " " << price << endl;
#endif #endif
} return price;
} else { } else {
throw_(download_error, throw_(download_error,
"Failed to download price for '" << commodity.symbol << "Failed to download price for '" << commodity.symbol() <<
"' (command: \"getquote " << commodity.symbol << "\")"); "' (command: \"getquote " << commodity.base_symbol() << "\")");
} }
} }

View file

@ -5,7 +5,7 @@
namespace ledger { namespace ledger {
class quotes_by_script : public commodity_base_t::updater_t class quotes_by_script
{ {
path price_db; path price_db;
time_duration pricing_leeway; 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), : price_db(_price_db), pricing_leeway(_pricing_leeway),
cache_dirty(_cache_dirty) {} cache_dirty(_cache_dirty) {}
virtual void operator()(commodity_base_t& commodity, virtual optional<amount_t>
const ptime& moment, operator()(commodity_t& commodity,
const ptime& date, const optional<moment_t>& date,
const ptime& last, const optional<moment_t>& moment,
amount_t& price); const optional<moment_t>& last);
}; };
DECLARE_EXCEPTION(download_error); DECLARE_EXCEPTION(download_error);

View file

@ -2,6 +2,41 @@
namespace ledger { 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, unsigned int session_t::read_journal(std::istream& in,
journal_t * journal, journal_t * journal,
account_t * master, 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 // jww (2007-04-26): All of Ledger should be accessed through a
// session_t object // session_t object
void initialize() static void initialize()
{ {
IF_VERIFY()
initialize_memory_tracing();
amount_t::initialize(); amount_t::initialize();
xml::xpath_t::initialize(); xml::xpath_t::initialize();
} }
void shutdown() static void shutdown()
{ {
xml::xpath_t::shutdown(); xml::xpath_t::shutdown();
amount_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 } // namespace ledger

View file

@ -10,6 +10,8 @@ namespace ledger {
class session_t : public xml::xpath_t::scope_t class session_t : public xml::xpath_t::scope_t
{ {
public: public:
static session_t * current;
path data_file; path data_file;
optional<path> init_file; optional<path> init_file;
optional<path> cache_file; optional<path> cache_file;
@ -185,8 +187,16 @@ class session_t : public xml::xpath_t::scope_t
#endif #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 } // namespace ledger

View file

@ -98,7 +98,7 @@ extern "C" {
#endif #endif
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
#include <boost/any.hpp> #include <boost/cast.hpp>
#include <boost/current_function.hpp> #include <boost/current_function.hpp>
#include <boost/date_time/posix_time/posix_time.hpp> #include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/filesystem/convenience.hpp> #include <boost/filesystem/convenience.hpp>
@ -106,11 +106,16 @@ extern "C" {
#include <boost/filesystem/fstream.hpp> #include <boost/filesystem/fstream.hpp>
#include <boost/filesystem/operations.hpp> #include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.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/operators.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <boost/ptr_container/ptr_list.hpp> #include <boost/ptr_container/ptr_list.hpp>
#include <boost/ptr_container/ptr_vector.hpp> #include <boost/ptr_container/ptr_vector.hpp>
#include <boost/regex.hpp> #include <boost/regex.hpp>
#include <boost/signal.hpp>
#endif // _SYSTEM_HH #endif // _SYSTEM_HH

View file

@ -263,9 +263,9 @@ transaction_t * parse_transaction(char * line,
if (xact->amount->commodity() && if (xact->amount->commodity() &&
! xact->amount->commodity().annotated) ! xact->amount->commodity().annotated)
xact->amount->annotate_commodity(per_unit_cost, xact->amount->annotate_commodity(annotation_t(per_unit_cost,
xact->entry->actual_date(), xact->entry->actual_date(),
xact->entry->code); xact->entry->code));
DEBUG("ledger.textual.parse", "line " << linenum << ": " << DEBUG("ledger.textual.parse", "line " << linenum << ": " <<
"Total cost is " << *xact->cost); "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" case 'D': { // a default commodity for "entry"
amount_t amt(skip_ws(line + 1)); amount_t amt(skip_ws(line + 1));
commodity_t::default_commodity = &amt.commodity(); amount_t::default_pool->default_commodity = &amt.commodity();
break; break;
} }
@ -756,7 +756,8 @@ unsigned int textual_parser_t::parse(std::istream& in,
parse_symbol(symbol_and_price, symbol); parse_symbol(symbol_and_price, symbol);
amount_t price(symbol_and_price); 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); commodity->add_price(datetime, price);
break; break;
} }
@ -766,7 +767,8 @@ unsigned int textual_parser_t::parse(std::istream& in,
string symbol; string symbol;
parse_symbol(p, 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); commodity->add_flags(COMMODITY_STYLE_NOMARKET);
break; break;
} }
@ -774,7 +776,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
case 'Y': // set current year case 'Y': // set current year
#if 0 #if 0
// jww (2007-04-18): Need to set this up again // 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 #endif
break; break;

View file

@ -1,6 +1,11 @@
#ifndef _UTILS_H #ifndef _UTILS_H
#define _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> #include <system.hh>
/********************************************************************** /**********************************************************************

View file

@ -1647,7 +1647,7 @@ void value_t::in_place_cast(type_t cast_type)
break; break;
} }
if (alldigits) { if (alldigits) {
long temp = lexical_cast<long>((*(string **) data)->c_str()); long temp = std::atol((*(string **) data)->c_str());
destroy(); destroy();
*(long *) data = temp; *(long *) data = temp;
} else { } else {
@ -1840,7 +1840,7 @@ bool value_t::realzero() const
return 0; 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) { switch (type) {
case BOOLEAN: 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"); throw_(value_error, "Cannot find the value of a date/time");
case INTEGER: case INTEGER:
return *this; return *this;
case AMOUNT:
return ((amount_t *) data)->value(moment); case AMOUNT: {
case BALANCE: if (optional<amount_t> val = ((amount_t *) data)->value(moment))
return ((balance_t *) data)->value(moment); return *val;
case BALANCE_PAIR: return false;
return ((balance_pair_t *) data)->quantity.value(moment); }
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: case STRING:
throw_(value_error, "Cannot find the value of a string"); throw_(value_error, "Cannot find the value of a string");
case XML_NODE: case XML_NODE:
return (*(xml::node_t **) data)->to_value().value(moment); return (*(xml::node_t **) data)->to_value().value(moment);
case POINTER: case POINTER:
throw_(value_error, "Cannot find the value of a pointer"); throw_(value_error, "Cannot find the value of a pointer");
case SEQUENCE: case SEQUENCE:
@ -1954,45 +1968,37 @@ value_t value_t::unround() const
return value_t(); return value_t();
} }
value_t value_t::price() const value_t value_t::annotated_price() const
{ {
switch (type) { switch (type) {
case BOOLEAN: 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: case INTEGER:
return *this; return *this;
case DATETIME: 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: { case AMOUNT: {
optional<amount_t> temp = ((amount_t *) data)->price(); optional<amount_t> temp = ((amount_t *) data)->annotation_details().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();
if (! temp) if (! temp)
return false; return false;
return *temp; 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: 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: case XML_NODE:
return (*(xml::node_t **) data)->to_value().price(); return (*(xml::node_t **) data)->to_value().annotated_price();
case POINTER: 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: case SEQUENCE:
throw_(value_error, "Cannot find the price of a sequence"); throw_(value_error, "Cannot find the annotated price of a sequence");
default: default:
assert(0); assert(0);
@ -2002,46 +2008,38 @@ value_t value_t::price() const
return value_t(); return value_t();
} }
value_t value_t::date() const value_t value_t::annotated_date() const
{ {
switch (type) { switch (type) {
case BOOLEAN: 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: 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: case DATETIME:
return *this; return *this;
case AMOUNT: { case AMOUNT: {
optional<moment_t> temp = ((amount_t *) data)->date(); optional<moment_t> temp = ((amount_t *) data)->annotation_details().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();
if (! temp) if (! temp)
return false; return false;
return *temp; 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: 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: case XML_NODE:
return (*(xml::node_t **) data)->to_value().date(); return (*(xml::node_t **) data)->to_value().annotated_date();
case POINTER: 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: case SEQUENCE:
throw_(value_error, "Cannot find the date of a sequence"); throw_(value_error, "Cannot find the annotated date of a sequence");
default: default:
assert(0); assert(0);
@ -2051,46 +2049,38 @@ value_t value_t::date() const
return value_t(); return value_t();
} }
value_t value_t::tag() const value_t value_t::annotated_tag() const
{ {
switch (type) { switch (type) {
case BOOLEAN: 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: 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: case DATETIME:
return *this; return *this;
case AMOUNT: { case AMOUNT: {
optional<string> temp = ((amount_t *) data)->tag(); optional<string> temp = ((amount_t *) data)->annotation_details().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();
if (! temp) if (! temp)
return false; return false;
return *temp; 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: 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: case XML_NODE:
return (*(xml::node_t **) data)->to_value().date(); return (*(xml::node_t **) data)->to_value().annotated_tag();
case POINTER: 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: case SEQUENCE:
throw_(value_error, "Cannot find the date of a sequence"); throw_(value_error, "Cannot find the annotated tag of a sequence");
default: default:
assert(0); assert(0);

View file

@ -363,9 +363,9 @@ class value_t
value_t abs() const; value_t abs() const;
void in_place_cast(type_t cast_type); void in_place_cast(type_t cast_type);
value_t cost() const; value_t cost() const;
value_t price() const; value_t annotated_price() const;
value_t date() const; value_t annotated_date() const;
value_t tag() const; value_t annotated_tag() const;
value_t cast(type_t cast_type) const { value_t cast(type_t cast_type) const {
value_t temp(*this); value_t temp(*this);
@ -379,7 +379,8 @@ class value_t
value_t& add(const amount_t& amount, value_t& add(const amount_t& amount,
const optional<amount_t>& cost = optional<amount_t>()); 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(); void in_place_reduce();
value_t reduce() const { value_t reduce() const {

View file

@ -104,7 +104,7 @@ static void endElement(void *userData, const char *name)
} }
else if (std::strcmp(name, "symbol") == 0) { else if (std::strcmp(name, "symbol") == 0) {
assert(! curr_comm); assert(! curr_comm);
curr_comm = commodity_t::find_or_create(data); curr_comm = amount_t::default_pool->find_or_create(data);
assert(curr_comm); assert(curr_comm);
curr_comm->add_flags(COMMODITY_STYLE_SUFFIXED); curr_comm->add_flags(COMMODITY_STYLE_SUFFIXED);
if (! comm_flags.empty()) { if (! comm_flags.empty()) {

View file

@ -654,7 +654,7 @@ xpath_t::parse_value_term(std::istream& in, unsigned short tflags) const
int id = -1; int id = -1;
if (std::isdigit(ident[0])) { if (std::isdigit(ident[0])) {
node.reset(new op_t(op_t::ARG_INDEX)); 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) { else if ((id = document_t::lookup_builtin_id(ident)) != -1) {
node.reset(new op_t(op_t::NODE_ID)); 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 (! symbol.empty()) {
if (commodity_t::find(symbol)) if (amount_t::default_pool->find(symbol))
out << '@'; out << '@';
out << symbol; out << symbol;
} }

View file

@ -3,10 +3,10 @@
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(BasicAmountTestCase, "numerics"); CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(BasicAmountTestCase, "numerics");
void BasicAmountTestCase::setUp() { void BasicAmountTestCase::setUp() {
ledger::initialize(); ledger::set_session_context(&session);
} }
void BasicAmountTestCase::tearDown() { void BasicAmountTestCase::tearDown() {
ledger::shutdown(); ledger::set_session_context();
} }
void BasicAmountTestCase::testConstructors() void BasicAmountTestCase::testConstructors()
@ -17,8 +17,8 @@ void BasicAmountTestCase::testConstructors()
amount_t x3(123.456); amount_t x3(123.456);
amount_t x5("123456"); amount_t x5("123456");
amount_t x6("123.456"); amount_t x6("123.456");
amount_t x7(string("123456")); amount_t x7(std::string("123456"));
amount_t x8(string("123.456")); amount_t x8(std::string("123.456"));
amount_t x9(x3); amount_t x9(x3);
amount_t x10(x6); amount_t x10(x6);
amount_t x11(x8); amount_t x11(x8);

View file

@ -33,6 +33,8 @@ class BasicAmountTestCase : public CPPUNIT_NS::TestCase
CPPUNIT_TEST_SUITE_END(); CPPUNIT_TEST_SUITE_END();
public: public:
ledger::session_t session;
BasicAmountTestCase() {} BasicAmountTestCase() {}
virtual ~BasicAmountTestCase() {} virtual ~BasicAmountTestCase() {}

View file

@ -3,10 +3,10 @@
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(CommodityTestCase, "numerics"); CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(CommodityTestCase, "numerics");
void CommodityTestCase::setUp() { void CommodityTestCase::setUp() {
ledger::initialize(); ledger::set_session_context(&session);
} }
void CommodityTestCase::tearDown() { void CommodityTestCase::tearDown() {
ledger::shutdown(); ledger::set_session_context();
} }
void CommodityTestCase::testPriceHistory() void CommodityTestCase::testPriceHistory()
@ -32,8 +32,13 @@ void CommodityTestCase::testPriceHistory()
aapl.add_price(mar01_07, amount_t("$19.50")); aapl.add_price(mar01_07, amount_t("$19.50"));
aapl.add_price(apr15_07, amount_t("$21.22")); aapl.add_price(apr15_07, amount_t("$21.22"));
assertEqual(amount_t("$1831.83"), x1.value(feb28_07sbm)); optional<amount_t> amt1 = x1.value(feb28_07sbm);
assertEqual(amount_t("$2124.12"), x1.value(now)); 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); assertValid(x1);
} }

View file

@ -15,6 +15,8 @@ class CommodityTestCase : public CPPUNIT_NS::TestCase
CPPUNIT_TEST_SUITE_END(); CPPUNIT_TEST_SUITE_END();
public: public:
ledger::session_t session;
CommodityTestCase() {} CommodityTestCase() {}
virtual ~CommodityTestCase() {} virtual ~CommodityTestCase() {}

View file

@ -6,7 +6,7 @@ CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(CommodityAmountTestCase, "numerics");
void CommodityAmountTestCase::setUp() void CommodityAmountTestCase::setUp()
{ {
ledger::initialize(); ledger::set_session_context(&session);
// Cause the display precision for dollars to be initialized to 2. // Cause the display precision for dollars to be initialized to 2.
amount_t x1("$1.00"); amount_t x1("$1.00");
@ -17,7 +17,8 @@ void CommodityAmountTestCase::setUp()
void CommodityAmountTestCase::tearDown() void CommodityAmountTestCase::tearDown()
{ {
amount_t::full_strings = false; amount_t::full_strings = false;
ledger::shutdown();
ledger::set_session_context();
} }
void CommodityAmountTestCase::testConstructors() void CommodityAmountTestCase::testConstructors()

View file

@ -28,6 +28,8 @@ class CommodityAmountTestCase : public CPPUNIT_NS::TestCase
CPPUNIT_TEST_SUITE_END(); CPPUNIT_TEST_SUITE_END();
public: public:
ledger::session_t session;
CommodityAmountTestCase() {} CommodityAmountTestCase() {}
virtual ~CommodityAmountTestCase() {} virtual ~CommodityAmountTestCase() {}