Revised how commodities are dealt with.
This commit is contained in:
parent
f9f24fab93
commit
c59018c29d
31 changed files with 1146 additions and 915 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
98
configure
vendored
|
|
@ -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'`\\"
|
||||
|
||||
|
|
|
|||
23
configure.in
23
configure.in
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
AC_PREREQ(2.59)
|
||||
|
||||
AC_INIT(ledger, 3.0-git-lexical_cast, johnw@newartisans.com)
|
||||
AC_INIT(ledger, 3.0-git-commodity_pool, johnw@newartisans.com)
|
||||
AC_CONFIG_SRCDIR(ledger)
|
||||
AM_INIT_AUTOMAKE
|
||||
|
||||
|
|
@ -148,6 +148,27 @@ else
|
|||
AC_MSG_FAILURE("Could not find boost_filesystem library (set CPPFLAGS and LDFLAGS?)")
|
||||
fi
|
||||
|
||||
# check for boost_signals
|
||||
AC_CACHE_CHECK(
|
||||
[if boost_signals is available],
|
||||
[boost_signals_cpplib_avail],
|
||||
[boost_signals_save_libs=$LIBS
|
||||
LIBS="-lboost_signals $LIBS"
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_TRY_LINK(
|
||||
[#include <boost/signal.hpp>],
|
||||
[boost::signal<void (void)> this_signal;],
|
||||
[boost_signals_cpplib_avail=true],
|
||||
[boost_signals_cpplib_avail=false])
|
||||
AC_LANG_POP
|
||||
LIBS=$boost_signals_save_libs])
|
||||
|
||||
if [test x$boost_signals_cpplib_avail = xtrue ]; then
|
||||
LIBS="-lboost_signals $LIBS"
|
||||
else
|
||||
AC_MSG_FAILURE("Could not find boost_signals library (set CPPFLAGS and LDFLAGS?)")
|
||||
fi
|
||||
|
||||
# check for gmp
|
||||
AC_CACHE_CHECK(
|
||||
[if libgmp is available],
|
||||
|
|
|
|||
256
src/amount.cc
256
src/amount.cc
|
|
@ -46,6 +46,8 @@
|
|||
|
||||
namespace ledger {
|
||||
|
||||
commodity_pool_t * amount_t::default_pool = NULL;
|
||||
|
||||
bool amount_t::keep_base = false;
|
||||
|
||||
bool amount_t::keep_price = false;
|
||||
|
|
@ -109,25 +111,12 @@ void amount_t::initialize()
|
|||
mpz_init(temp);
|
||||
mpz_init(divisor);
|
||||
|
||||
// jww (2007-05-02): Be very careful here!
|
||||
if (! default_pool)
|
||||
default_pool = new commodity_pool_t;
|
||||
|
||||
true_value = new amount_t::bigint_t;
|
||||
mpz_set_ui(true_value->val, 1);
|
||||
|
||||
commodity_base_t::updater = NULL;
|
||||
|
||||
commodity_t::commodities_by_ident = new commodities_array;
|
||||
|
||||
commodity_t::default_commodity = NULL;
|
||||
commodity_t::null_commodity = commodity_t::create("");
|
||||
commodity_t::null_commodity->add_flags(COMMODITY_STYLE_NOMARKET |
|
||||
COMMODITY_STYLE_BUILTIN);
|
||||
|
||||
// Add time commodity conversions, so that timelog's may be parsed
|
||||
// in terms of seconds, but reported as minutes or hours.
|
||||
commodity_t * commodity = commodity_t::create("s");
|
||||
commodity->add_flags(COMMODITY_STYLE_NOMARKET | COMMODITY_STYLE_BUILTIN);
|
||||
|
||||
parse_conversion("1.0m", "60s");
|
||||
parse_conversion("1.0h", "60m");
|
||||
}
|
||||
|
||||
void amount_t::shutdown()
|
||||
|
|
@ -135,30 +124,12 @@ void amount_t::shutdown()
|
|||
mpz_clear(temp);
|
||||
mpz_clear(divisor);
|
||||
|
||||
if (commodity_base_t::updater) {
|
||||
checked_delete(commodity_base_t::updater);
|
||||
commodity_base_t::updater = NULL;
|
||||
// jww (2007-05-02): Be very careful here!
|
||||
if (default_pool) {
|
||||
checked_delete(default_pool);
|
||||
default_pool = NULL;
|
||||
}
|
||||
|
||||
for (base_commodities_map::iterator i = commodity_base_t::commodities.begin();
|
||||
i != commodity_base_t::commodities.end();
|
||||
i++)
|
||||
checked_delete((*i).second);
|
||||
|
||||
for (commodities_map::iterator i = commodity_t::commodities.begin();
|
||||
i != commodity_t::commodities.end();
|
||||
i++)
|
||||
checked_delete((*i).second);
|
||||
|
||||
commodity_base_t::commodities.clear();
|
||||
commodity_t::commodities.clear();
|
||||
|
||||
checked_delete(commodity_t::commodities_by_ident);
|
||||
commodity_t::commodities_by_ident = NULL;
|
||||
|
||||
commodity_t::null_commodity = NULL;
|
||||
commodity_t::default_commodity = NULL;
|
||||
|
||||
true_value->ref--;
|
||||
assert(true_value->ref == 0);
|
||||
checked_delete(true_value);
|
||||
|
|
@ -408,9 +379,9 @@ amount_t& amount_t::operator+=(const amount_t& amt)
|
|||
if (commodity() != amt.commodity())
|
||||
throw_(amount_error,
|
||||
"Adding amounts with different commodities: " <<
|
||||
(has_commodity() ? commodity_->qualified_symbol : "NONE") <<
|
||||
(has_commodity() ? commodity().symbol() : "NONE") <<
|
||||
" != " <<
|
||||
(amt.has_commodity() ? amt.commodity_->qualified_symbol : "NONE"));
|
||||
(amt.has_commodity() ? amt.commodity().symbol() : "NONE"));
|
||||
|
||||
if (! amt.quantity)
|
||||
return *this;
|
||||
|
|
@ -443,9 +414,9 @@ amount_t& amount_t::operator-=(const amount_t& amt)
|
|||
if (commodity() != amt.commodity())
|
||||
throw_(amount_error,
|
||||
"Subtracting amounts with different commodities: " <<
|
||||
(has_commodity() ? commodity_->qualified_symbol : "NONE") <<
|
||||
(has_commodity() ? commodity().symbol() : "NONE") <<
|
||||
" != " <<
|
||||
(amt.has_commodity() ? amt.commodity_->qualified_symbol : "NONE"));
|
||||
(amt.has_commodity() ? amt.commodity().symbol() : "NONE"));
|
||||
|
||||
if (! amt.quantity)
|
||||
return *this;
|
||||
|
|
@ -529,9 +500,9 @@ amount_t& amount_t::operator*=(const amount_t& amt)
|
|||
commodity() != amt.commodity())
|
||||
throw_(amount_error,
|
||||
"Multiplying amounts with different commodities: " <<
|
||||
(has_commodity() ? commodity_->qualified_symbol : "NONE") <<
|
||||
(has_commodity() ? commodity().symbol() : "NONE") <<
|
||||
" != " <<
|
||||
(amt.has_commodity() ? amt.commodity_->qualified_symbol : "NONE"));
|
||||
(amt.has_commodity() ? amt.commodity().symbol() : "NONE"));
|
||||
|
||||
if (! amt.quantity) {
|
||||
*this = *this - *this; // preserve our commodity
|
||||
|
|
@ -569,9 +540,9 @@ amount_t& amount_t::operator/=(const amount_t& amt)
|
|||
commodity() != amt.commodity())
|
||||
throw_(amount_error,
|
||||
"Dividing amounts with different commodities: " <<
|
||||
(has_commodity() ? commodity_->qualified_symbol : "NONE") <<
|
||||
(has_commodity() ? commodity().symbol() : "NONE") <<
|
||||
" != " <<
|
||||
(amt.has_commodity() ? amt.commodity_->qualified_symbol : "NONE"));
|
||||
(amt.has_commodity() ? amt.commodity().symbol() : "NONE"));
|
||||
|
||||
if (! amt.quantity || ! amt) {
|
||||
throw_(amount_error, "Divide by zero");
|
||||
|
|
@ -686,14 +657,14 @@ amount_t& amount_t::in_place_unreduce()
|
|||
return *this;
|
||||
}
|
||||
|
||||
amount_t amount_t::value(const moment_t& moment) const
|
||||
optional<amount_t> amount_t::value(const optional<moment_t>& moment) const
|
||||
{
|
||||
if (quantity) {
|
||||
amount_t amt(commodity().value(moment));
|
||||
if (! amt.realzero())
|
||||
return (amt * number()).round();
|
||||
optional<amount_t> amt(commodity().value(moment));
|
||||
if (amt)
|
||||
return (*amt * number()).round();
|
||||
}
|
||||
return *this;
|
||||
return optional<amount_t>();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -756,34 +727,29 @@ long amount_t::to_long() const
|
|||
}
|
||||
|
||||
|
||||
void amount_t::annotate_commodity(const optional<amount_t>& tprice,
|
||||
const optional<moment_t>& tdate,
|
||||
const optional<string>& ttag)
|
||||
void amount_t::annotate_commodity(const annotation_t& details)
|
||||
{
|
||||
const commodity_t * this_base;
|
||||
commodity_t * this_base;
|
||||
annotated_commodity_t * this_ann = NULL;
|
||||
|
||||
if (commodity().annotated) {
|
||||
this_ann = &static_cast<annotated_commodity_t&>(commodity());
|
||||
this_base = this_ann->ptr;
|
||||
this_ann = &commodity().as_annotated();
|
||||
this_base = &this_ann->referent();
|
||||
} else {
|
||||
this_base = &commodity();
|
||||
}
|
||||
assert(this_base);
|
||||
|
||||
DEBUG("amounts.commodities", "Annotating commodity for amount "
|
||||
<< *this << std::endl
|
||||
<< " price " << (tprice ? tprice->to_string() : "NONE") << " "
|
||||
<< " date " << (tdate ? *tdate : moment_t()) << " "
|
||||
<< " ttag " << (ttag ? *ttag : "NONE"));
|
||||
<< *this << std::endl << details);
|
||||
|
||||
if (commodity_t * ann_comm =
|
||||
annotated_commodity_t::find_or_create
|
||||
(*this_base,
|
||||
! tprice && this_ann ? this_ann->price : tprice,
|
||||
! tdate && this_ann ? this_ann->date : tdate,
|
||||
! ttag && this_ann ? this_ann->tag : ttag))
|
||||
this_base->parent().find_or_create(*this_base, details))
|
||||
set_commodity(*ann_comm);
|
||||
#ifdef ASSERTS_ON
|
||||
else
|
||||
assert(false);
|
||||
#endif
|
||||
|
||||
DEBUG("amounts.commodities", " Annotated amount is " << *this);
|
||||
}
|
||||
|
|
@ -802,69 +768,48 @@ amount_t amount_t::strip_annotations(const bool _keep_price,
|
|||
<< " keep date " << _keep_date << " "
|
||||
<< " keep tag " << _keep_tag);
|
||||
|
||||
annotated_commodity_t&
|
||||
ann_comm(static_cast<annotated_commodity_t&>(commodity()));
|
||||
assert(ann_comm.base);
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
152
src/amount.h
152
src/amount.h
|
|
@ -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
|
||||
|
|
|
|||
128
src/balance.cc
128
src/balance.cc
|
|
@ -133,7 +133,8 @@ balance_t& balance_t::operator/=(const amount_t& amt)
|
|||
return *this;
|
||||
}
|
||||
|
||||
amount_t balance_t::amount(const commodity_t& commodity) const
|
||||
optional<amount_t>
|
||||
balance_t::amount(const optional<const commodity_t&>& commodity) const
|
||||
{
|
||||
if (! commodity) {
|
||||
if (amounts.size() == 1) {
|
||||
|
|
@ -151,71 +152,27 @@ amount_t balance_t::amount(const commodity_t& commodity) const
|
|||
}
|
||||
}
|
||||
else if (amounts.size() > 0) {
|
||||
amounts_map::const_iterator i = amounts.find(&commodity);
|
||||
amounts_map::const_iterator i = amounts.find(&*commodity);
|
||||
if (i != amounts.end())
|
||||
return (*i).second;
|
||||
}
|
||||
return amount_t();
|
||||
return optional<amount_t>();
|
||||
}
|
||||
|
||||
balance_t balance_t::value(const moment_t& moment) const
|
||||
{
|
||||
balance_t temp;
|
||||
|
||||
for (amounts_map::const_iterator i = amounts.begin();
|
||||
i != amounts.end();
|
||||
i++)
|
||||
temp += (*i).second.value(moment);
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
optional<balance_t> balance_t::price() const
|
||||
optional<balance_t>
|
||||
balance_t::value(const optional<moment_t>& moment) const
|
||||
{
|
||||
optional<balance_t> temp;
|
||||
|
||||
for (amounts_map::const_iterator i = amounts.begin();
|
||||
i != amounts.end();
|
||||
i++) {
|
||||
optional<amount_t> i_price = (*i).second.price();
|
||||
if (i_price) {
|
||||
i++)
|
||||
if (optional<amount_t> val = (*i).second.value(moment)) {
|
||||
if (! temp)
|
||||
temp = balance_t();
|
||||
*temp += *i_price;
|
||||
*temp += *val;
|
||||
}
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
optional<moment_t> balance_t::date() const
|
||||
{
|
||||
optional<moment_t> temp;
|
||||
|
||||
for (amounts_map::const_iterator i = amounts.begin();
|
||||
i != amounts.end();
|
||||
i++) {
|
||||
optional<moment_t> tdate = (*i).second.date();
|
||||
if (! temp && tdate)
|
||||
temp = *tdate;
|
||||
else if (temp && tdate && temp != tdate)
|
||||
return optional<moment_t>();
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
optional<string> balance_t::tag() const
|
||||
{
|
||||
optional<string> temp;
|
||||
|
||||
for (amounts_map::const_iterator i = amounts.begin();
|
||||
i != amounts.end();
|
||||
i++) {
|
||||
optional<string> ttag = (*i).second.tag();
|
||||
if (! temp && ttag)
|
||||
temp = *ttag;
|
||||
else if (temp && ttag && temp != ttag)
|
||||
return optional<string>();
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
|
|
@ -243,52 +200,33 @@ void balance_t::write(std::ostream& out,
|
|||
if (lwidth == -1)
|
||||
lwidth = first_width;
|
||||
|
||||
if (commodity_t::commodities_sorted) {
|
||||
for (amounts_map::const_iterator i = amounts.begin();
|
||||
i != amounts.end();
|
||||
i++) {
|
||||
int width;
|
||||
if (! first) {
|
||||
out << std::endl;
|
||||
width = lwidth;
|
||||
} else {
|
||||
first = false;
|
||||
width = first_width;
|
||||
}
|
||||
typedef std::vector<const amount_t *> amounts_array;
|
||||
amounts_array sorted;
|
||||
|
||||
out.width(width);
|
||||
out.fill(' ');
|
||||
out << std::right << (*i).second;
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
553
src/commodity.cc
553
src/commodity.cc
|
|
@ -43,61 +43,98 @@
|
|||
|
||||
namespace ledger {
|
||||
|
||||
#ifndef THREADSAFE
|
||||
base_commodities_map commodity_base_t::commodities;
|
||||
|
||||
commodity_base_t::updater_t * commodity_base_t::updater = NULL;
|
||||
|
||||
commodities_map commodity_t::commodities;
|
||||
commodities_array * commodity_t::commodities_by_ident;
|
||||
bool commodity_t::commodities_sorted = false;
|
||||
commodity_t * commodity_t::null_commodity;
|
||||
commodity_t * commodity_t::default_commodity = NULL;
|
||||
#endif
|
||||
|
||||
void commodity_base_t::add_price(const moment_t& date,
|
||||
const amount_t& price)
|
||||
void commodity_t::add_price(const moment_t& date,
|
||||
const amount_t& price)
|
||||
{
|
||||
if (! history)
|
||||
history = history_t();
|
||||
if (! base->history)
|
||||
base->history = history_t();
|
||||
|
||||
history_map::iterator i = history->prices.find(date);
|
||||
if (i != history->prices.end()) {
|
||||
history_map::iterator i = base->history->prices.find(date);
|
||||
if (i != base->history->prices.end()) {
|
||||
(*i).second = price;
|
||||
} else {
|
||||
std::pair<history_map::iterator, bool> result
|
||||
= history->prices.insert(history_pair(date, price));
|
||||
= base->history->prices.insert(history_pair(date, price));
|
||||
assert(result.second);
|
||||
}
|
||||
}
|
||||
|
||||
bool commodity_base_t::remove_price(const moment_t& date)
|
||||
bool commodity_t::remove_price(const moment_t& date)
|
||||
{
|
||||
if (history) {
|
||||
history_map::size_type n = history->prices.erase(date);
|
||||
if (base->history) {
|
||||
history_map::size_type n = base->history->prices.erase(date);
|
||||
if (n > 0) {
|
||||
if (history->prices.empty())
|
||||
history.reset();
|
||||
if (base->history->prices.empty())
|
||||
base->history.reset();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
commodity_base_t * commodity_base_t::create(const string& symbol)
|
||||
optional<amount_t> commodity_t::value(const optional<moment_t>& moment)
|
||||
{
|
||||
commodity_base_t * commodity = new commodity_base_t(symbol);
|
||||
optional<moment_t> age;
|
||||
optional<amount_t> price;
|
||||
|
||||
DEBUG("amounts.commodities", "Creating base commodity " << symbol);
|
||||
if (base->history) {
|
||||
assert(base->history->prices.size() > 0);
|
||||
|
||||
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
|
||||
|
|
|
|||
405
src/commodity.h
405
src/commodity.h
|
|
@ -53,232 +53,229 @@ namespace ledger {
|
|||
#define COMMODITY_STYLE_NOMARKET 0x0010
|
||||
#define COMMODITY_STYLE_BUILTIN 0x0020
|
||||
|
||||
typedef std::map<const moment_t, amount_t> history_map;
|
||||
typedef std::pair<const moment_t, amount_t> history_pair;
|
||||
|
||||
class commodity_base_t;
|
||||
|
||||
typedef std::map<const string, commodity_base_t *> base_commodities_map;
|
||||
typedef std::pair<const string, commodity_base_t *> base_commodities_pair;
|
||||
|
||||
class commodity_base_t : public noncopyable
|
||||
{
|
||||
public:
|
||||
private:
|
||||
friend class commodity_pool_t;
|
||||
friend class commodity_t;
|
||||
friend class annotated_commodity_t;
|
||||
|
||||
friend void amount_t::initialize();
|
||||
friend void amount_t::shutdown();
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
24
src/main.cc
24
src/main.cc
|
|
@ -408,26 +408,26 @@ int main(int argc, char * argv[], char * envp[])
|
|||
#if defined(TRACING_ON)
|
||||
if (i + 1 < argc && std::strcmp(argv[i], "--trace") == 0) {
|
||||
ledger::_log_level = LOG_TRACE;
|
||||
ledger::_trace_level = lexical_cast<int>(argv[i + 1]);
|
||||
ledger::_trace_level = std::atoi(argv[i + 1]);
|
||||
i++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
IF_VERIFY()
|
||||
initialize_memory_tracing();
|
||||
|
||||
try {
|
||||
std::ios::sync_with_stdio(false);
|
||||
boost::filesystem::path::default_name_check
|
||||
(boost::filesystem::portable_posix_name);
|
||||
|
||||
ledger::initialize();
|
||||
|
||||
#if ! defined(FULL_DEBUG)
|
||||
ledger::do_cleanup = false;
|
||||
#endif
|
||||
INFO("Ledger starting");
|
||||
|
||||
std::auto_ptr<ledger::session_t> session(new ledger::session_t);
|
||||
|
||||
ledger::set_session_context(session.get());
|
||||
|
||||
#if 0
|
||||
session->register_parser(new binary_parser_t);
|
||||
#endif
|
||||
|
|
@ -445,9 +445,11 @@ int main(int argc, char * argv[], char * envp[])
|
|||
|
||||
status = read_and_report(report.get(), argc, argv, envp);
|
||||
|
||||
IF_VERIFY() {
|
||||
if (! DO_VERIFY()) {
|
||||
report.release();
|
||||
session.release();
|
||||
} else {
|
||||
ledger::set_session_context();
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
|
|
@ -480,8 +482,12 @@ int main(int argc, char * argv[], char * envp[])
|
|||
status = _status;
|
||||
}
|
||||
|
||||
IF_VERIFY()
|
||||
ledger::shutdown();
|
||||
IF_VERIFY() {
|
||||
INFO("Ledger ended (Boost/libstdc++ may still hold memory)");
|
||||
shutdown_memory_tracing();
|
||||
} else {
|
||||
INFO("Ledger ended");
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ unsigned int qif_parser_t::parse(std::istream& in,
|
|||
unsigned char prec = xact->amount->commodity().precision();
|
||||
|
||||
if (! def_commodity) {
|
||||
def_commodity = commodity_t::find_or_create("$");
|
||||
def_commodity = amount_t::default_pool->find_or_create("$");
|
||||
assert(def_commodity);
|
||||
}
|
||||
xact->amount->set_commodity(*def_commodity);
|
||||
|
|
|
|||
|
|
@ -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() << "\")");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
12
src/quotes.h
12
src/quotes.h
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
namespace ledger {
|
||||
|
||||
class quotes_by_script : public commodity_base_t::updater_t
|
||||
class quotes_by_script
|
||||
{
|
||||
path price_db;
|
||||
time_duration pricing_leeway;
|
||||
|
|
@ -18,11 +18,11 @@ class quotes_by_script : public commodity_base_t::updater_t
|
|||
: price_db(_price_db), pricing_leeway(_pricing_leeway),
|
||||
cache_dirty(_cache_dirty) {}
|
||||
|
||||
virtual void operator()(commodity_base_t& commodity,
|
||||
const ptime& moment,
|
||||
const ptime& date,
|
||||
const ptime& last,
|
||||
amount_t& price);
|
||||
virtual optional<amount_t>
|
||||
operator()(commodity_t& commodity,
|
||||
const optional<moment_t>& date,
|
||||
const optional<moment_t>& moment,
|
||||
const optional<moment_t>& last);
|
||||
};
|
||||
|
||||
DECLARE_EXCEPTION(download_error);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
/**********************************************************************
|
||||
|
|
|
|||
126
src/value.cc
126
src/value.cc
|
|
@ -1647,7 +1647,7 @@ void value_t::in_place_cast(type_t cast_type)
|
|||
break;
|
||||
}
|
||||
if (alldigits) {
|
||||
long temp = lexical_cast<long>((*(string **) data)->c_str());
|
||||
long temp = std::atol((*(string **) data)->c_str());
|
||||
destroy();
|
||||
*(long *) data = temp;
|
||||
} else {
|
||||
|
|
@ -1840,7 +1840,7 @@ bool value_t::realzero() const
|
|||
return 0;
|
||||
}
|
||||
|
||||
value_t value_t::value(const moment_t& moment) const
|
||||
value_t value_t::value(const optional<moment_t>& moment) const
|
||||
{
|
||||
switch (type) {
|
||||
case BOOLEAN:
|
||||
|
|
@ -1849,16 +1849,30 @@ value_t value_t::value(const moment_t& moment) const
|
|||
throw_(value_error, "Cannot find the value of a date/time");
|
||||
case INTEGER:
|
||||
return *this;
|
||||
case AMOUNT:
|
||||
return ((amount_t *) data)->value(moment);
|
||||
case BALANCE:
|
||||
return ((balance_t *) data)->value(moment);
|
||||
case BALANCE_PAIR:
|
||||
return ((balance_pair_t *) data)->quantity.value(moment);
|
||||
|
||||
case AMOUNT: {
|
||||
if (optional<amount_t> val = ((amount_t *) data)->value(moment))
|
||||
return *val;
|
||||
return false;
|
||||
}
|
||||
case BALANCE: {
|
||||
if (optional<balance_t> bal = ((balance_t *) data)->value(moment))
|
||||
return *bal;
|
||||
return false;
|
||||
}
|
||||
case BALANCE_PAIR: {
|
||||
if (optional<balance_t> bal =
|
||||
((balance_pair_t *) data)->quantity.value(moment))
|
||||
return *bal;
|
||||
return false;
|
||||
}
|
||||
|
||||
case STRING:
|
||||
throw_(value_error, "Cannot find the value of a string");
|
||||
|
||||
case XML_NODE:
|
||||
return (*(xml::node_t **) data)->to_value().value(moment);
|
||||
|
||||
case POINTER:
|
||||
throw_(value_error, "Cannot find the value of a pointer");
|
||||
case SEQUENCE:
|
||||
|
|
@ -1954,45 +1968,37 @@ value_t value_t::unround() const
|
|||
return value_t();
|
||||
}
|
||||
|
||||
value_t value_t::price() const
|
||||
value_t value_t::annotated_price() const
|
||||
{
|
||||
switch (type) {
|
||||
case BOOLEAN:
|
||||
throw_(value_error, "Cannot find the price of a boolean");
|
||||
throw_(value_error, "Cannot find the annotated price of a boolean");
|
||||
case INTEGER:
|
||||
return *this;
|
||||
case DATETIME:
|
||||
throw_(value_error, "Cannot find the price of a date/time");
|
||||
throw_(value_error, "Cannot find the annotated price of a date/time");
|
||||
|
||||
case AMOUNT: {
|
||||
optional<amount_t> temp = ((amount_t *) data)->price();
|
||||
if (! temp)
|
||||
return false;
|
||||
return *temp;
|
||||
}
|
||||
case BALANCE: {
|
||||
optional<balance_t> temp = ((balance_t *) data)->price();
|
||||
if (! temp)
|
||||
return false;
|
||||
return *temp;
|
||||
}
|
||||
case BALANCE_PAIR: {
|
||||
optional<balance_t> temp = ((balance_pair_t *) data)->price();
|
||||
optional<amount_t> temp = ((amount_t *) data)->annotation_details().price;
|
||||
if (! temp)
|
||||
return false;
|
||||
return *temp;
|
||||
}
|
||||
|
||||
case BALANCE:
|
||||
throw_(value_error, "Cannot find the annotated price of a balance");
|
||||
case BALANCE_PAIR:
|
||||
throw_(value_error, "Cannot find the annotated price of a balance pair");
|
||||
case STRING:
|
||||
throw_(value_error, "Cannot find the price of a string");
|
||||
throw_(value_error, "Cannot find the annotated price of a string");
|
||||
|
||||
case XML_NODE:
|
||||
return (*(xml::node_t **) data)->to_value().price();
|
||||
return (*(xml::node_t **) data)->to_value().annotated_price();
|
||||
|
||||
case POINTER:
|
||||
throw_(value_error, "Cannot find the price of a pointer");
|
||||
throw_(value_error, "Cannot find the annotated price of a pointer");
|
||||
case SEQUENCE:
|
||||
throw_(value_error, "Cannot find the price of a sequence");
|
||||
throw_(value_error, "Cannot find the annotated price of a sequence");
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
|
|
@ -2002,46 +2008,38 @@ value_t value_t::price() const
|
|||
return value_t();
|
||||
}
|
||||
|
||||
value_t value_t::date() const
|
||||
value_t value_t::annotated_date() const
|
||||
{
|
||||
switch (type) {
|
||||
case BOOLEAN:
|
||||
throw_(value_error, "Cannot find the date of a boolean");
|
||||
throw_(value_error, "Cannot find the annotated date of a boolean");
|
||||
case INTEGER:
|
||||
throw_(value_error, "Cannot find the date of an integer");
|
||||
throw_(value_error, "Cannot find the annotated date of an integer");
|
||||
|
||||
case DATETIME:
|
||||
return *this;
|
||||
|
||||
case AMOUNT: {
|
||||
optional<moment_t> temp = ((amount_t *) data)->date();
|
||||
if (! temp)
|
||||
return false;
|
||||
return *temp;
|
||||
}
|
||||
case BALANCE: {
|
||||
optional<moment_t> temp = ((balance_t *) data)->date();
|
||||
if (! temp)
|
||||
return false;
|
||||
return *temp;
|
||||
}
|
||||
case BALANCE_PAIR: {
|
||||
optional<moment_t> temp = ((balance_pair_t *) data)->date();
|
||||
optional<moment_t> temp = ((amount_t *) data)->annotation_details().date;
|
||||
if (! temp)
|
||||
return false;
|
||||
return *temp;
|
||||
}
|
||||
|
||||
case BALANCE:
|
||||
throw_(value_error, "Cannot find the annotated date of a balance");
|
||||
case BALANCE_PAIR:
|
||||
throw_(value_error, "Cannot find the annotated date of a balance pair");
|
||||
case STRING:
|
||||
throw_(value_error, "Cannot find the date of a string");
|
||||
throw_(value_error, "Cannot find the annotated date of a string");
|
||||
|
||||
case XML_NODE:
|
||||
return (*(xml::node_t **) data)->to_value().date();
|
||||
return (*(xml::node_t **) data)->to_value().annotated_date();
|
||||
|
||||
case POINTER:
|
||||
throw_(value_error, "Cannot find the date of a pointer");
|
||||
throw_(value_error, "Cannot find the annotated date of a pointer");
|
||||
case SEQUENCE:
|
||||
throw_(value_error, "Cannot find the date of a sequence");
|
||||
throw_(value_error, "Cannot find the annotated date of a sequence");
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
|
|
@ -2051,46 +2049,38 @@ value_t value_t::date() const
|
|||
return value_t();
|
||||
}
|
||||
|
||||
value_t value_t::tag() const
|
||||
value_t value_t::annotated_tag() const
|
||||
{
|
||||
switch (type) {
|
||||
case BOOLEAN:
|
||||
throw_(value_error, "Cannot find the date of a boolean");
|
||||
throw_(value_error, "Cannot find the annotated tag of a boolean");
|
||||
case INTEGER:
|
||||
throw_(value_error, "Cannot find the date of an integer");
|
||||
throw_(value_error, "Cannot find the annotated tag of an integer");
|
||||
|
||||
case DATETIME:
|
||||
return *this;
|
||||
|
||||
case AMOUNT: {
|
||||
optional<string> temp = ((amount_t *) data)->tag();
|
||||
if (! temp)
|
||||
return false;
|
||||
return *temp;
|
||||
}
|
||||
case BALANCE: {
|
||||
optional<string> temp = ((balance_t *) data)->tag();
|
||||
if (! temp)
|
||||
return false;
|
||||
return *temp;
|
||||
}
|
||||
case BALANCE_PAIR: {
|
||||
optional<string> temp = ((balance_pair_t *) data)->tag();
|
||||
optional<string> temp = ((amount_t *) data)->annotation_details().tag;
|
||||
if (! temp)
|
||||
return false;
|
||||
return *temp;
|
||||
}
|
||||
|
||||
case BALANCE:
|
||||
throw_(value_error, "Cannot find the annotated tag of a balance");
|
||||
case BALANCE_PAIR:
|
||||
throw_(value_error, "Cannot find the annotated tag of a balance pair");
|
||||
case STRING:
|
||||
throw_(value_error, "Cannot find the date of a string");
|
||||
throw_(value_error, "Cannot find the annotated tag of a string");
|
||||
|
||||
case XML_NODE:
|
||||
return (*(xml::node_t **) data)->to_value().date();
|
||||
return (*(xml::node_t **) data)->to_value().annotated_tag();
|
||||
|
||||
case POINTER:
|
||||
throw_(value_error, "Cannot find the date of a pointer");
|
||||
throw_(value_error, "Cannot find the annotated tag of a pointer");
|
||||
case SEQUENCE:
|
||||
throw_(value_error, "Cannot find the date of a sequence");
|
||||
throw_(value_error, "Cannot find the annotated tag of a sequence");
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ class BasicAmountTestCase : public CPPUNIT_NS::TestCase
|
|||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
ledger::session_t session;
|
||||
|
||||
BasicAmountTestCase() {}
|
||||
virtual ~BasicAmountTestCase() {}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ class CommodityTestCase : public CPPUNIT_NS::TestCase
|
|||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
ledger::session_t session;
|
||||
|
||||
CommodityTestCase() {}
|
||||
virtual ~CommodityTestCase() {}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ class CommodityAmountTestCase : public CPPUNIT_NS::TestCase
|
|||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
ledger::session_t session;
|
||||
|
||||
CommodityAmountTestCase() {}
|
||||
virtual ~CommodityAmountTestCase() {}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue