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
PYLIBS = pyledger ledger gdtoa gmp boost_date_time \
boost_filesystem boost_regex boost_python
boost_signals boost_filesystem boost_regex boost_python
if HAVE_EXPAT
PYLIBS += expat

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

98
configure vendored
View file

@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.61 for ledger 3.0-git-lexical_cast.
# Generated by GNU Autoconf 2.61 for ledger 3.0-git-commodity_pool.
#
# Report bugs to <johnw@newartisans.com>.
#
@ -728,8 +728,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
# Identity of this package.
PACKAGE_NAME='ledger'
PACKAGE_TARNAME='ledger'
PACKAGE_VERSION='3.0-git-lexical_cast'
PACKAGE_STRING='ledger 3.0-git-lexical_cast'
PACKAGE_VERSION='3.0-git-commodity_pool'
PACKAGE_STRING='ledger 3.0-git-commodity_pool'
PACKAGE_BUGREPORT='johnw@newartisans.com'
ac_unique_file="ledger"
@ -1424,7 +1424,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures ledger 3.0-git-lexical_cast to adapt to many kinds of systems.
\`configure' configures ledger 3.0-git-commodity_pool to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1494,7 +1494,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of ledger 3.0-git-lexical_cast:";;
short | recursive ) echo "Configuration of ledger 3.0-git-commodity_pool:";;
esac
cat <<\_ACEOF
@ -1605,7 +1605,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
ledger configure 3.0-git-lexical_cast
ledger configure 3.0-git-commodity_pool
generated by GNU Autoconf 2.61
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@ -1619,7 +1619,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by ledger $as_me 3.0-git-lexical_cast, which was
It was created by ledger $as_me 3.0-git-commodity_pool, which was
generated by GNU Autoconf 2.61. Invocation command line was
$ $0 $@
@ -2310,7 +2310,7 @@ fi
# Define the identity of the package.
PACKAGE='ledger'
VERSION='3.0-git-lexical_cast'
VERSION='3.0-git-commodity_pool'
cat >>confdefs.h <<_ACEOF
@ -19691,6 +19691,84 @@ See \`config.log' for more details." >&2;}
{ (exit 1); exit 1; }; }
fi
# check for boost_signals
{ echo "$as_me:$LINENO: checking if boost_signals is available" >&5
echo $ECHO_N "checking if boost_signals is available... $ECHO_C" >&6; }
if test "${boost_signals_cpplib_avail+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
boost_signals_save_libs=$LIBS
LIBS="-lboost_signals $LIBS"
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#include <boost/signal.hpp>
int
main ()
{
boost::signal<void (void)> this_signal;
;
return 0;
}
_ACEOF
rm -f conftest.$ac_objext conftest$ac_exeext
if { (ac_try="$ac_link"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_link") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && {
test -z "$ac_cxx_werror_flag" ||
test ! -s conftest.err
} && test -s conftest$ac_exeext &&
$as_test_x conftest$ac_exeext; then
boost_signals_cpplib_avail=true
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
boost_signals_cpplib_avail=false
fi
rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
LIBS=$boost_signals_save_libs
fi
{ echo "$as_me:$LINENO: result: $boost_signals_cpplib_avail" >&5
echo "${ECHO_T}$boost_signals_cpplib_avail" >&6; }
if test x$boost_signals_cpplib_avail = xtrue ; then
LIBS="-lboost_signals $LIBS"
else
{ { echo "$as_me:$LINENO: error: \"Could not find boost_signals library (set CPPFLAGS and LDFLAGS?)\"
See \`config.log' for more details." >&5
echo "$as_me: error: \"Could not find boost_signals library (set CPPFLAGS and LDFLAGS?)\"
See \`config.log' for more details." >&2;}
{ (exit 1); exit 1; }; }
fi
# check for gmp
{ echo "$as_me:$LINENO: checking if libgmp is available" >&5
echo $ECHO_N "checking if libgmp is available... $ECHO_C" >&6; }
@ -21967,7 +22045,7 @@ exec 6>&1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by ledger $as_me 3.0-git-lexical_cast, which was
This file was extended by ledger $as_me 3.0-git-commodity_pool, which was
generated by GNU Autoconf 2.61. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@ -22020,7 +22098,7 @@ Report bugs to <bug-autoconf@gnu.org>."
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF
ac_cs_version="\\
ledger config.status 3.0-git-lexical_cast
ledger config.status 3.0-git-commodity_pool
configured by $0, generated by GNU Autoconf 2.61,
with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"

View file

@ -3,7 +3,7 @@
AC_PREREQ(2.59)
AC_INIT(ledger, 3.0-git-lexical_cast, johnw@newartisans.com)
AC_INIT(ledger, 3.0-git-commodity_pool, johnw@newartisans.com)
AC_CONFIG_SRCDIR(ledger)
AM_INIT_AUTOMAKE
@ -148,6 +148,27 @@ else
AC_MSG_FAILURE("Could not find boost_filesystem library (set CPPFLAGS and LDFLAGS?)")
fi
# check for boost_signals
AC_CACHE_CHECK(
[if boost_signals is available],
[boost_signals_cpplib_avail],
[boost_signals_save_libs=$LIBS
LIBS="-lboost_signals $LIBS"
AC_LANG_PUSH(C++)
AC_TRY_LINK(
[#include <boost/signal.hpp>],
[boost::signal<void (void)> this_signal;],
[boost_signals_cpplib_avail=true],
[boost_signals_cpplib_avail=false])
AC_LANG_POP
LIBS=$boost_signals_save_libs])
if [test x$boost_signals_cpplib_avail = xtrue ]; then
LIBS="-lboost_signals $LIBS"
else
AC_MSG_FAILURE("Could not find boost_signals library (set CPPFLAGS and LDFLAGS?)")
fi
# check for gmp
AC_CACHE_CHECK(
[if libgmp is available],

View file

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

View file

@ -52,6 +52,8 @@
namespace ledger {
class commodity_t;
class annotation_t;
class commodity_pool_t;
DECLARE_EXCEPTION(amount_error);
@ -87,6 +89,12 @@ public:
static void initialize();
static void shutdown();
/**
* The default_pool is a static variable indicating which commodity
* pool should be used when none is specified.
*/
static commodity_pool_t * default_pool;
/**
* The `keep_base' member determines whether scalable commodities
* are automatically converted to their most reduced form when
@ -291,11 +299,12 @@ public:
* compact form greater than 1.0. That is, 3599s will unreduce to
* 59.98m, while 3601 unreduces to 1h.
*
* value(moment_t) returns the history value of an amount, based on
* the price history of its commodity. For example, if the amount
* were 10 AAPL, and on Apr 10, 2000 each share of AAPL was worth
* $10, then call value() for that moment in time would yield the
* amount $100.00.
* value(optional<moment_t>) returns the historical value for an
* amount -- the default moment returns the most recently known
* price -- based on the price history of its commodity. For
* example, if the amount were 10 AAPL, and on Apr 10, 2000 each
* share of AAPL was worth $10, then call value() for that moment in
* time would yield the amount $100.00.
*
* Further, for the sake of efficiency and avoiding temporary
* objects, the following methods support "in-place" variants that
@ -341,7 +350,8 @@ public:
}
amount_t& in_place_unreduce();
amount_t value(const moment_t& moment) const;
optional<amount_t> value(const optional<moment_t>& moment =
optional<moment_t>()) const;
/**
* Truth tests. An amount may be truth test in several ways:
@ -466,38 +476,26 @@ public:
* the price argument is required, although it can be passed as
* `optional<amount_t>()' if no price is desired.
*
* commodity_annotated() returns true if an amount's commodity has
* any annotation details associated with it.
*
* annotation_details() returns all of the details of an annotated
* commodity's annotations. The structure returns will evaluate as
* boolean false if there are no details.
*
* strip_annotations([keep_price, keep_date, keep_tag]) returns an
* amount whose commodity's annotations have been stripped. The
* three `keep_' arguments determine which annotation detailed are
* kept, meaning that the default is to follow whatever
* amount_t::keep_price, amount_t::keep_date and amount_t::keep_tag
* have been set to (which all default to false).
*
* price() returns an amount's annotated commodity's price. This
* return value is of type `optional<amount_t>', so it must be
* tested for boolean truth to determine if an annotated price even
* existed.
*
* date() returns an amount's annotated commodity's date. This
* return value is of type `optional<moment_t>'.
*
* tag() returns an amount's annotated commodity's tag. This return
* value is of type `optional<string>'.
*/
void annotate_commodity(const optional<amount_t>& tprice,
const optional<moment_t>& tdate = optional<moment_t>(),
const optional<string>& ttag = optional<string>());
amount_t strip_annotations(const bool _keep_price = keep_price,
const bool _keep_date = keep_date,
const bool _keep_tag = keep_tag) const;
optional<amount_t> price() const;
optional<moment_t> date() const;
optional<string> tag() const;
#define AMOUNT_PARSE_NO_MIGRATE 0x01
#define AMOUNT_PARSE_NO_REDUCE 0x02
void annotate_commodity(const annotation_t& details);
bool commodity_annotated() const;
annotation_t annotation_details() const;
amount_t strip_annotations(const bool _keep_price = keep_price,
const bool _keep_date = keep_date,
const bool _keep_tag = keep_tag) const;
/**
* Parsing methods. The method `parse' is used to parse an amount
@ -505,11 +503,18 @@ public:
* defined which simply calls parse on the input stream. The
* `parse' method has two forms:
*
* parse(istream, unsigned char flags) parses an amount from the
* given input stream.
* parse(commodity_pool_t, istream, flags_t) parses an
* amount from the given input stream, registering commodity details
* according to the commodity pool which is passed in as the first
* parameter.
*
* parse(string, unsigned char flags) parses an amount from the
* given string.
* parse(istream, flags_t) is the same as the preceding function,
* 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
* following:
@ -538,15 +543,36 @@ public:
* amount_t::parse_conversion("1.0m", "60s"); // a minute is 60 seconds
* amount_t::parse_conversion("1.0h", "60m"); // an hour is 60 minutes
*/
void parse(std::istream& in, unsigned char flags = 0);
void parse(const string& str, unsigned char flags = 0) {
#define AMOUNT_PARSE_NO_MIGRATE 0x01
#define AMOUNT_PARSE_NO_REDUCE 0x02
typedef uint_least8_t flags_t;
void parse(commodity_pool_t& parent, std::istream& in, flags_t flags = 0);
void parse(commodity_pool_t& parent, const string& str, flags_t flags = 0) {
std::istringstream stream(str);
parse(stream, flags);
parse(parent, stream, flags);
}
static void parse_conversion(const string& larger_str,
void parse(std::istream& in, flags_t flags = 0) {
assert(default_pool);
parse(*default_pool, in, flags);
}
void parse(const string& str, flags_t flags = 0) {
assert(default_pool);
parse(*default_pool, str, flags);
}
static void parse_conversion(commodity_pool_t& parent,
const string& larger_str,
const string& smaller_str);
static void parse_conversion(const string& larger_str,
const string& smaller_str) {
assert(default_pool);
parse_conversion(*default_pool, larger_str, smaller_str);
}
/**
* Printing methods. An amount may be output to a stream using the
* `print' method. There is also a global operator<< defined which
@ -571,18 +597,40 @@ public:
* input stream or a character pointer, and it may be serialized to
* an output stream. The methods used are:
*
* read(istream) reads an amount from the given input stream. It
* must have been put there using `write(ostream)'.
* read(commodity_pool_t, istream) reads an amount from the given
* input stream. It must have been put there using
* `write(ostream)'. Also, the given pool must be exactly what it
* was at the time the amount was `written'. Thus, the required
* flow of logic is:
* amount.write(out)
* pool.write(out)
* pool.read(in)
* amount.read(pool, in)
*
* read(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(istream) does the same as read, only it relies on
* `amount_t::default_pool' to specify the pool.
*
* read(commodity_pool_t, char *&) reads an amount from data which
* has been read from an input stream into a buffer. it advances
* the pointer passed in to the end of the deserialized amount.
*
* read(char *&) does the same as read, only it relies on
* `amount_t::default_pool' to specify the pool.
*
* write(ostream) writes an amount to an output stream in a compact
* binary format.
*/
void read(std::istream& in);
void read(char *& data);
void read(commodity_pool_t& parent, std::istream& in);
void read(commodity_pool_t& parent, char *& data);
void read(std::istream& in) {
assert(default_pool);
read(*default_pool, in);
}
void read(char *& data) {
assert(default_pool);
read(*default_pool, data);
}
void write(std::ostream& out) const;
@ -614,10 +662,9 @@ public:
bool valid() const;
private:
friend void parse_annotations(std::istream& in,
optional<amount_t>& price,
optional<moment_t>& date,
optional<string>& tag);
friend bool parse_annotations(commodity_pool_t& parent,
std::istream& in,
annotation_t& details);
};
inline amount_t amount_t::exact(const string& value) {
@ -672,11 +719,12 @@ inline amount_t amount_t::round() const {
}
inline bool amount_t::has_commodity() const {
return commodity_ && commodity_ != commodity_t::null_commodity;
return commodity_ && commodity_ != commodity_->parent().null_commodity;
}
inline commodity_t& amount_t::commodity() const {
return has_commodity() ? *commodity_ : *commodity_t::null_commodity;
// jww (2007-05-02): Should be a way to access null_commodity better
return has_commodity() ? *commodity_ : *default_pool->null_commodity;
}
} // namespace ledger

View file

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

View file

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

View file

@ -43,61 +43,98 @@
namespace ledger {
#ifndef THREADSAFE
base_commodities_map commodity_base_t::commodities;
commodity_base_t::updater_t * commodity_base_t::updater = NULL;
commodities_map commodity_t::commodities;
commodities_array * commodity_t::commodities_by_ident;
bool commodity_t::commodities_sorted = false;
commodity_t * commodity_t::null_commodity;
commodity_t * commodity_t::default_commodity = NULL;
#endif
void commodity_base_t::add_price(const moment_t& date,
const amount_t& price)
void commodity_t::add_price(const moment_t& date,
const amount_t& price)
{
if (! history)
history = history_t();
if (! base->history)
base->history = history_t();
history_map::iterator i = history->prices.find(date);
if (i != history->prices.end()) {
history_map::iterator i = base->history->prices.find(date);
if (i != base->history->prices.end()) {
(*i).second = price;
} else {
std::pair<history_map::iterator, bool> result
= history->prices.insert(history_pair(date, price));
= base->history->prices.insert(history_pair(date, price));
assert(result.second);
}
}
bool commodity_base_t::remove_price(const moment_t& date)
bool commodity_t::remove_price(const moment_t& date)
{
if (history) {
history_map::size_type n = history->prices.erase(date);
if (base->history) {
history_map::size_type n = base->history->prices.erase(date);
if (n > 0) {
if (history->prices.empty())
history.reset();
if (base->history->prices.empty())
base->history.reset();
return true;
}
}
return false;
}
commodity_base_t * commodity_base_t::create(const string& symbol)
optional<amount_t> commodity_t::value(const optional<moment_t>& moment)
{
commodity_base_t * commodity = new commodity_base_t(symbol);
optional<moment_t> age;
optional<amount_t> price;
DEBUG("amounts.commodities", "Creating base commodity " << symbol);
if (base->history) {
assert(base->history->prices.size() > 0);
std::pair<base_commodities_map::iterator, bool> result
= commodities.insert(base_commodities_pair(symbol, commodity));
assert(result.second);
if (! moment) {
history_map::reverse_iterator r = base->history->prices.rbegin();
age = (*r).first;
price = (*r).second;
} else {
history_map::iterator i = base->history->prices.lower_bound(*moment);
if (i == base->history->prices.end()) {
history_map::reverse_iterator r = base->history->prices.rbegin();
age = (*r).first;
price = (*r).second;
} else {
age = (*i).first;
if (*moment != *age) {
if (i != base->history->prices.begin()) {
--i;
age = (*i).first;
price = (*i).second;
} else {
age = optional<moment_t>();
}
} else {
price = (*i).second;
}
}
}
}
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++)
if (std::isspace(*p) || std::isdigit(*p) || *p == '-' || *p == '.')
@ -108,7 +145,7 @@ bool commodity_t::needs_quotes(const string& symbol)
bool commodity_t::valid() const
{
if (symbol().empty() && this != null_commodity) {
if (symbol().empty() && this != parent().null_commodity) {
DEBUG("ledger.validate",
"commodity_t: symbol().empty() && this != null_commodity");
return false;
@ -127,224 +164,34 @@ bool commodity_t::valid() const
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
{
// If the base commodities don't match, the game's up.
if (base != comm.base)
return false;
if (price &&
(! comm.annotated ||
price != static_cast<const annotated_commodity_t&>(comm).price))
assert(annotated);
if (! comm.annotated)
return false;
if (date &&
(! 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))
if (details != comm.as_annotated().details)
return false;
return true;
}
void
annotated_commodity_t::write_annotations(std::ostream& out,
const optional<amount_t>& price,
const optional<moment_t>& date,
const optional<string>& tag)
annotated_commodity_t::write_annotations(std::ostream& out,
const annotation_t& info)
{
if (price)
out << " {" << *price << '}';
if (info.price)
out << " {" << *info.price << '}';
if (date)
out << " [" << *date << ']';
if (info.date)
out << " [" << *info.date << ']';
if (tag)
out << " (" << *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);
if (info.tag)
out << " (" << *info.tag << ')';
}
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& arightcomm(static_cast<annotated_commodity_t&>(rightcomm));
if (! aleftcomm.price && arightcomm.price)
if (! aleftcomm.details.price && arightcomm.details.price)
return true;
if (aleftcomm.price && ! arightcomm.price)
if (aleftcomm.details.price && ! arightcomm.details.price)
return false;
if (aleftcomm.price && arightcomm.price) {
amount_t leftprice(*aleftcomm.price);
if (aleftcomm.details.price && arightcomm.details.price) {
amount_t leftprice(*aleftcomm.details.price);
leftprice.in_place_reduce();
amount_t rightprice(*arightcomm.price);
amount_t rightprice(*arightcomm.details.price);
rightprice.in_place_reduce();
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;
if (aleftcomm.date && ! arightcomm.date)
if (aleftcomm.details.date && ! arightcomm.details.date)
return false;
if (aleftcomm.date && arightcomm.date) {
duration_t diff = *aleftcomm.date - *arightcomm.date;
if (aleftcomm.details.date && arightcomm.details.date) {
duration_t diff = *aleftcomm.details.date - *arightcomm.details.date;
return diff.is_negative();
}
if (! aleftcomm.tag && arightcomm.tag)
if (! aleftcomm.details.tag && arightcomm.details.tag)
return true;
if (aleftcomm.tag && ! arightcomm.tag)
if (aleftcomm.details.tag && ! arightcomm.details.tag)
return false;
if (aleftcomm.tag && arightcomm.tag)
return *aleftcomm.tag < *arightcomm.tag;
if (aleftcomm.details.tag && arightcomm.details.tag)
return *aleftcomm.details.tag < *arightcomm.details.tag;
assert(false);
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

View file

@ -53,232 +53,229 @@ namespace ledger {
#define COMMODITY_STYLE_NOMARKET 0x0010
#define COMMODITY_STYLE_BUILTIN 0x0020
typedef std::map<const moment_t, amount_t> history_map;
typedef std::pair<const moment_t, amount_t> history_pair;
class commodity_base_t;
typedef std::map<const string, commodity_base_t *> base_commodities_map;
typedef std::pair<const string, commodity_base_t *> base_commodities_pair;
class commodity_base_t : public noncopyable
{
public:
private:
friend class commodity_pool_t;
friend class commodity_t;
friend class annotated_commodity_t;
friend void amount_t::initialize();
friend void amount_t::shutdown();
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;
typedef std::map<const moment_t, amount_t> history_map;
typedef std::pair<const moment_t, amount_t> history_pair;
struct history_t {
history_map prices;
ptime last_lookup;
history_t() : last_lookup() {}
};
optional<history_t> history;
void add_price(const moment_t& date, const amount_t& price);
bool remove_price(const moment_t& date);
amount_t value(const moment_t& moment = now);
typedef uint_least8_t flags_t;
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:
class updater_t {
public:
virtual ~updater_t() {}
virtual void operator()(commodity_base_t& commodity,
const moment_t& moment,
const moment_t& date,
const moment_t& last,
amount_t& price) = 0;
};
friend class updater_t;
static updater_t * updater;
explicit commodity_base_t()
: flags(COMMODITY_STYLE_DEFAULTS), precision(0) {
TRACE_CTOR(commodity_base_t, "");
}
explicit commodity_base_t
(const string& _symbol,
amount_t::precision_t _precision = 0,
unsigned int _flags = COMMODITY_STYLE_DEFAULTS)
: flags(_flags), symbol(_symbol), precision(_precision) {
TRACE_CTOR(commodity_base_t,
"const string&, amount_t::precision_t, unsigned int");
}
~commodity_base_t() {
TRACE_DTOR(commodity_base_t);
}
};
typedef std::map<const string, commodity_t *> commodities_map;
typedef std::pair<const string, commodity_t *> commodities_pair;
typedef std::vector<commodity_t *> commodities_array;
class annotated_commodity_t;
class commodity_t
: public equality_comparable<commodity_t, noncopyable>
: public equality_comparable1<commodity_t, noncopyable>
{
friend class annotated_commodity_t;
public:
// This map remembers all commodities that have been defined.
static bool symbol_needs_quotes(const string& symbol);
static commodities_map commodities;
static commodities_array * commodities_by_ident;
static bool commodities_sorted;
static commodity_t * null_commodity;
static commodity_t * default_commodity;
typedef commodity_base_t::flags_t flags_t;
typedef commodity_base_t::history_t history_t;
typedef commodity_base_t::history_map history_map;
typedef commodity_base_t::history_pair history_pair;
typedef uint_least32_t ident_t;
static commodity_t * create(const string& symbol);
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;
shared_ptr<commodity_base_t> base;
commodity_pool_t * parent_;
ident_t ident;
commodity_base_t * base;
string qualified_symbol;
optional<string> qualified_symbol;
optional<string> mapping_key_;
bool annotated;
public:
explicit commodity_t() : base(NULL), annotated(false) {
explicit commodity_t(commodity_pool_t * _parent,
const shared_ptr<commodity_base_t>& _base)
: base(_base), parent_(_parent), annotated(false) {
TRACE_CTOR(commodity_t, "");
}
commodity_t(const commodity_t& o)
: ident(o.ident), base(o.base),
qualified_symbol(o.qualified_symbol), annotated(o.annotated) {
TRACE_CTOR(commodity_t, "copy");
}
virtual ~commodity_t() {
TRACE_DTOR(commodity_t);
}
operator bool() const {
return this != null_commodity;
}
operator bool() const;
virtual bool operator==(const commodity_t& comm) const {
if (comm.annotated)
return comm == *this;
return base == comm.base;
return base.get() == comm.base.get();
}
commodity_pool_t& parent() const {
return *parent_;
}
annotated_commodity_t& as_annotated();
const annotated_commodity_t& as_annotated() const;
string base_symbol() const {
return base->symbol;
}
string symbol() const {
return qualified_symbol;
return qualified_symbol ? *qualified_symbol : base_symbol();
}
void write(std::ostream& out) const {
out << symbol();
string mapping_key() const {
if (mapping_key_)
return *mapping_key_;
else
return base_symbol();
}
string name() const {
optional<string> name() const {
return base->name;
}
void set_name(const string& arg) {
void set_name(const optional<string>& arg = optional<string>()) {
base->name = arg;
}
string note() const {
optional<string> note() const {
return base->note;
}
void set_note(const string& arg) {
void set_note(const optional<string>& arg = optional<string>()) {
base->note = arg;
}
unsigned char precision() const {
amount_t::precision_t precision() const {
return base->precision;
}
void set_precision(unsigned char arg) {
void set_precision(amount_t::precision_t arg) {
base->precision = arg;
}
unsigned char flags() const {
flags_t flags() const {
return base->flags;
}
void set_flags(unsigned char arg) {
void set_flags(flags_t arg) {
base->flags = arg;
}
void add_flags(unsigned char arg) {
void add_flags(flags_t arg) {
base->flags |= arg;
}
void drop_flags(unsigned char arg) {
void drop_flags(flags_t arg) {
base->flags &= ~arg;
}
optional<amount_t> smaller() const {
return base->smaller;
}
void set_smaller(const amount_t& arg) {
void set_smaller(const optional<amount_t>& arg = optional<amount_t>()) {
base->smaller = arg;
}
optional<amount_t> larger() const {
return base->larger;
}
void set_larger(const amount_t& arg) {
void set_larger(const optional<amount_t>& arg = optional<amount_t>()) {
base->larger = arg;
}
optional<commodity_base_t::history_t> history() const {
optional<history_t> history() const {
return base->history;
}
void add_price(const moment_t& date, const amount_t& price) {
return base->add_price(date, price);
}
bool remove_price(const moment_t& date) {
return base->remove_price(date);
}
amount_t value(const moment_t& moment = now) const {
return base->value(moment);
void add_price(const moment_t& date, const amount_t& price);
bool remove_price(const moment_t& date);
optional<amount_t> value(const optional<moment_t>& moment =
optional<moment_t>());
void write(std::ostream& out) const {
out << symbol();
}
bool valid() const;
};
class annotated_commodity_t : public commodity_t
inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) {
comm.write(out);
return out;
}
struct annotation_t : public equality_comparable<annotation_t>
{
public:
const commodity_t * ptr;
optional<amount_t> price;
optional<moment_t> date;
optional<string> tag;
optional<amount_t> price;
optional<moment_t> date;
optional<string> tag;
explicit annotation_t(const optional<amount_t>& _price = optional<amount_t>(),
const optional<moment_t>& _date = optional<moment_t>(),
const optional<string>& _tag = optional<string>())
: price(_price), date(_date), tag(_tag) {}
explicit annotated_commodity_t() {
operator bool() const {
return price || date || tag;
}
bool operator==(const annotation_t& rhs) const {
return (price == rhs.price &&
date == rhs.date &&
tag == rhs.tag);
}
void write(std::ostream& out) const {
out << "price " << (price ? price->to_string() : "NONE") << " "
<< "date " << (date ? *date : moment_t()) << " "
<< "tag " << (tag ? *tag : "NONE");
}
bool valid() const {
assert(*this);
}
};
inline std::ostream& operator<<(std::ostream& out, const annotation_t& details) {
details.write(out);
return out;
}
class annotated_commodity_t
: public commodity_t,
equality_comparable1<annotated_commodity_t, noncopyable>
{
public:
commodity_t * ptr;
annotation_t details;
explicit annotated_commodity_t(commodity_t * _ptr,
const annotation_t& _details)
: commodity_t(_ptr->parent_, _ptr->base), ptr(_ptr), details(_details) {
TRACE_CTOR(annotated_commodity_t, "");
annotated = true;
}
@ -288,39 +285,131 @@ class annotated_commodity_t : public commodity_t
virtual bool operator==(const commodity_t& comm) const;
void write_annotations(std::ostream& out) const {
annotated_commodity_t::write_annotations(out, price, date, tag);
commodity_t& referent() {
return *ptr;
}
const commodity_t& referent() const {
return *ptr;
}
static void write_annotations(std::ostream& out,
const optional<amount_t>& price,
const optional<moment_t>& date,
const optional<string>& tag);
void write_annotations(std::ostream& out) const {
annotated_commodity_t::write_annotations(out, details);
}
private:
static commodity_t * create(const commodity_t& comm,
const optional<amount_t>& price,
const optional<moment_t>& date,
const optional<string>& tag,
const string& mapping_key);
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;
static void write_annotations(std::ostream& out,
const annotation_t& info);
};
inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) {
out << comm.symbol();
return out;
}
struct compare_amount_commodities {
bool operator()(const amount_t * left, const amount_t * right) const;
};
class commodity_pool_t : public noncopyable
{
public:
/**
* The commodities collection in commodity_pool_t maintains pointers
* to all the commodities which have ever been created by the user,
* whether explicitly by calling the create methods of
* commodity_pool_t, or implicitly by parsing a commoditized amount.
*
* The `commodities' member variable represents a collection which
* is indexed by two vertices: first, and ordered sequence of unique
* integer which identify commodities by a numerical identifier; and
* second, by a hashed set of symbolic names which reflect how the
* commodity was referred to by the user.
*/
typedef multi_index_container<
commodity_t *,
multi_index::indexed_by<
multi_index::ordered_unique<
multi_index::member<commodity_t,
commodity_t::ident_t, &commodity_t::ident> >,
multi_index::hashed_unique<
multi_index::const_mem_fun<commodity_t,
string, &commodity_t::mapping_key> >
>
> commodities_t;
commodities_t commodities;
commodity_t * null_commodity;
commodity_t * default_commodity;
private:
template<typename T>
struct first_initialized
{
typedef T result_type;
template<typename InputIterator>
T operator()(InputIterator first, InputIterator last) const
{
for (; first != last; first++)
if (*first)
return *first;
return T();
}
};
public:
boost::signal<optional<amount_t>
(commodity_t& commodity,
const optional<moment_t>& date,
const optional<moment_t>& moment,
const optional<moment_t>& last),
first_initialized<optional<amount_t> > > get_quote;
explicit commodity_pool_t();
~commodity_pool_t() {
typedef commodity_pool_t::commodities_t::nth_index<0>::type
commodities_by_ident;
commodities_by_ident& ident_index = commodities.get<0>();
for (commodities_by_ident::iterator i = ident_index.begin();
i != ident_index.end();
i++)
checked_delete(*i);
}
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
#endif // _COMMODITY_H

View file

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

View file

@ -115,8 +115,9 @@ bool entry_base_t::finalize()
annotated_commodity_t&
ann_comm(static_cast<annotated_commodity_t&>
((*x)->amount->commodity()));
if (ann_comm.price)
balance += *ann_comm.price * (*x)->amount->number() - *((*x)->cost);
if (ann_comm.details.price)
balance += (*ann_comm.details.price * (*x)->amount->number() -
*((*x)->cost));
}
} else {
saw_null = true;
@ -173,9 +174,9 @@ bool entry_base_t::finalize()
if ((*x)->amount->commodity() &&
! (*x)->amount->commodity().annotated)
(*x)->amount->annotate_commodity
(per_unit_cost.abs(),
entry ? entry->actual_date() : optional<moment_t>(),
entry ? entry->code : optional<string>());
(annotation_t(per_unit_cost.abs(),
entry ? entry->actual_date() : optional<moment_t>(),
entry ? entry->code : optional<string>()));
(*x)->cost = - (per_unit_cost * (*x)->amount->number());
balance += *(*x)->cost;
@ -610,14 +611,6 @@ bool journal_t::valid() const
return false;
}
for (commodities_map::const_iterator i = commodity_t::commodities.begin();
i != commodity_t::commodities.end();
i++)
if (! (*i).second->valid()) {
DEBUG("ledger.validate", "journal_t: commodity not valid");
return false;
}
return true;
}

View file

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

View file

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

View file

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

View file

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

View file

@ -2,6 +2,41 @@
namespace ledger {
session_t * session_t::current = NULL;
#if 0
boost::mutex session_t::session_mutex;
#endif
static void initialize();
static void shutdown();
void set_session_context(session_t * session)
{
#if 0
session_t::session_mutex.lock();
#endif
if (session && ! session_t::current) {
initialize();
}
else if (! session && session_t::current) {
shutdown();
#if 0
session_t::session_mutex.unlock();
#endif
}
session_t::current = session;
}
void release_session_context()
{
#if 0
session_t::session_mutex.unlock();
#endif
}
unsigned int session_t::read_journal(std::istream& in,
journal_t * journal,
account_t * master,
@ -203,26 +238,16 @@ xml::xpath_t::op_t * session_t::lookup(const string& name)
// jww (2007-04-26): All of Ledger should be accessed through a
// session_t object
void initialize()
static void initialize()
{
IF_VERIFY()
initialize_memory_tracing();
amount_t::initialize();
xml::xpath_t::initialize();
}
void shutdown()
static void shutdown()
{
xml::xpath_t::shutdown();
amount_t::shutdown();
IF_VERIFY() {
INFO("Ledger ended (Boost/libstdc++ may still hold memory)");
shutdown_memory_tracing();
} else {
INFO("Ledger ended");
}
}
} // namespace ledger

View file

@ -10,6 +10,8 @@ namespace ledger {
class session_t : public xml::xpath_t::scope_t
{
public:
static session_t * current;
path data_file;
optional<path> init_file;
optional<path> cache_file;
@ -185,8 +187,16 @@ class session_t : public xml::xpath_t::scope_t
#endif
};
void initialize();
void shutdown();
/**
* This sets the current session context, transferring all static
* globals to point at the data structures related to this session.
* Although Ledger itself is not thread-safe, by locking, switching
* session context, then unlocking after the operation is done,
* multiple threads can sequentially make use of the library. Thus, a
* session_t maintains all of the information relating to a single
* usage of the Ledger library.
*/
void set_session_context(session_t * session = NULL);
} // namespace ledger

View file

@ -98,7 +98,7 @@ extern "C" {
#endif
#include <boost/algorithm/string/predicate.hpp>
#include <boost/any.hpp>
#include <boost/cast.hpp>
#include <boost/current_function.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/filesystem/convenience.hpp>
@ -106,11 +106,16 @@ extern "C" {
#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/function.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/key_extractors.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/operators.hpp>
#include <boost/optional.hpp>
#include <boost/ptr_container/ptr_list.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
#include <boost/regex.hpp>
#include <boost/signal.hpp>
#endif // _SYSTEM_HH

View file

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

View file

@ -1,6 +1,11 @@
#ifndef _UTILS_H
#define _UTILS_H
#if defined(FULL_DEBUG)
#define BOOST_MULTI_INDEX_ENABLE_SAFE_MODE 1
#define BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING 1
#endif
#include <system.hh>
/**********************************************************************

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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