summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/amount.cc256
-rw-r--r--src/amount.h152
-rw-r--r--src/balance.cc126
-rw-r--r--src/balance.h40
-rw-r--r--src/commodity.cc543
-rw-r--r--src/commodity.h393
-rw-r--r--src/gnucash.cc4
-rw-r--r--src/journal.cc19
-rw-r--r--src/main.cc24
-rw-r--r--src/qif.cc2
-rw-r--r--src/quotes.cc69
-rw-r--r--src/quotes.h12
-rw-r--r--src/session.cc49
-rw-r--r--src/session.h14
-rw-r--r--src/system.hh9
-rw-r--r--src/textual.cc16
-rw-r--r--src/utils.h5
-rw-r--r--src/value.cc126
-rw-r--r--src/value.h9
-rw-r--r--src/xmlparse.cc2
-rw-r--r--src/xpath.cc4
21 files changed, 997 insertions, 877 deletions
diff --git a/src/amount.cc b/src/amount.cc
index b1ef1c03..6b9cfdd3 100644
--- a/src/amount.cc
+++ b/src/amount.cc
@@ -46,6 +46,8 @@
namespace ledger {
+commodity_pool_t * amount_t::default_pool = NULL;
+
bool amount_t::keep_base = false;
bool amount_t::keep_price = false;
@@ -109,25 +111,12 @@ void amount_t::initialize()
mpz_init(temp);
mpz_init(divisor);
+ // jww (2007-05-02): Be very careful here!
+ if (! default_pool)
+ default_pool = new commodity_pool_t;
+
true_value = new amount_t::bigint_t;
mpz_set_ui(true_value->val, 1);
-
- commodity_base_t::updater = NULL;
-
- commodity_t::commodities_by_ident = new commodities_array;
-
- commodity_t::default_commodity = NULL;
- commodity_t::null_commodity = commodity_t::create("");
- commodity_t::null_commodity->add_flags(COMMODITY_STYLE_NOMARKET |
- COMMODITY_STYLE_BUILTIN);
-
- // Add time commodity conversions, so that timelog's may be parsed
- // in terms of seconds, but reported as minutes or hours.
- commodity_t * commodity = commodity_t::create("s");
- commodity->add_flags(COMMODITY_STYLE_NOMARKET | COMMODITY_STYLE_BUILTIN);
-
- parse_conversion("1.0m", "60s");
- parse_conversion("1.0h", "60m");
}
void amount_t::shutdown()
@@ -135,30 +124,12 @@ void amount_t::shutdown()
mpz_clear(temp);
mpz_clear(divisor);
- if (commodity_base_t::updater) {
- checked_delete(commodity_base_t::updater);
- commodity_base_t::updater = NULL;
+ // jww (2007-05-02): Be very careful here!
+ if (default_pool) {
+ checked_delete(default_pool);
+ default_pool = NULL;
}
- for (base_commodities_map::iterator i = commodity_base_t::commodities.begin();
- i != commodity_base_t::commodities.end();
- i++)
- checked_delete((*i).second);
-
- for (commodities_map::iterator i = commodity_t::commodities.begin();
- i != commodity_t::commodities.end();
- i++)
- checked_delete((*i).second);
-
- commodity_base_t::commodities.clear();
- commodity_t::commodities.clear();
-
- checked_delete(commodity_t::commodities_by_ident);
- commodity_t::commodities_by_ident = NULL;
-
- commodity_t::null_commodity = NULL;
- commodity_t::default_commodity = NULL;
-
true_value->ref--;
assert(true_value->ref == 0);
checked_delete(true_value);
@@ -408,9 +379,9 @@ amount_t& amount_t::operator+=(const amount_t& amt)
if (commodity() != amt.commodity())
throw_(amount_error,
"Adding amounts with different commodities: " <<
- (has_commodity() ? commodity_->qualified_symbol : "NONE") <<
+ (has_commodity() ? commodity().symbol() : "NONE") <<
" != " <<
- (amt.has_commodity() ? amt.commodity_->qualified_symbol : "NONE"));
+ (amt.has_commodity() ? amt.commodity().symbol() : "NONE"));
if (! amt.quantity)
return *this;
@@ -443,9 +414,9 @@ amount_t& amount_t::operator-=(const amount_t& amt)
if (commodity() != amt.commodity())
throw_(amount_error,
"Subtracting amounts with different commodities: " <<
- (has_commodity() ? commodity_->qualified_symbol : "NONE") <<
+ (has_commodity() ? commodity().symbol() : "NONE") <<
" != " <<
- (amt.has_commodity() ? amt.commodity_->qualified_symbol : "NONE"));
+ (amt.has_commodity() ? amt.commodity().symbol() : "NONE"));
if (! amt.quantity)
return *this;
@@ -529,9 +500,9 @@ amount_t& amount_t::operator*=(const amount_t& amt)
commodity() != amt.commodity())
throw_(amount_error,
"Multiplying amounts with different commodities: " <<
- (has_commodity() ? commodity_->qualified_symbol : "NONE") <<
+ (has_commodity() ? commodity().symbol() : "NONE") <<
" != " <<
- (amt.has_commodity() ? amt.commodity_->qualified_symbol : "NONE"));
+ (amt.has_commodity() ? amt.commodity().symbol() : "NONE"));
if (! amt.quantity) {
*this = *this - *this; // preserve our commodity
@@ -569,9 +540,9 @@ amount_t& amount_t::operator/=(const amount_t& amt)
commodity() != amt.commodity())
throw_(amount_error,
"Dividing amounts with different commodities: " <<
- (has_commodity() ? commodity_->qualified_symbol : "NONE") <<
+ (has_commodity() ? commodity().symbol() : "NONE") <<
" != " <<
- (amt.has_commodity() ? amt.commodity_->qualified_symbol : "NONE"));
+ (amt.has_commodity() ? amt.commodity().symbol() : "NONE"));
if (! amt.quantity || ! amt) {
throw_(amount_error, "Divide by zero");
@@ -686,14 +657,14 @@ amount_t& amount_t::in_place_unreduce()
return *this;
}
-amount_t amount_t::value(const moment_t& moment) const
+optional<amount_t> amount_t::value(const optional<moment_t>& moment) const
{
if (quantity) {
- amount_t amt(commodity().value(moment));
- if (! amt.realzero())
- return (amt * number()).round();
+ optional<amount_t> amt(commodity().value(moment));
+ if (amt)
+ return (*amt * number()).round();
}
- return *this;
+ return optional<amount_t>();
}
@@ -756,34 +727,29 @@ long amount_t::to_long() const
}
-void amount_t::annotate_commodity(const optional<amount_t>& tprice,
- const optional<moment_t>& tdate,
- const optional<string>& ttag)
+void amount_t::annotate_commodity(const annotation_t& details)
{
- const commodity_t * this_base;
+ commodity_t * this_base;
annotated_commodity_t * this_ann = NULL;
if (commodity().annotated) {
- this_ann = &static_cast<annotated_commodity_t&>(commodity());
- this_base = this_ann->ptr;
+ this_ann = &commodity().as_annotated();
+ this_base = &this_ann->referent();
} else {
this_base = &commodity();
}
assert(this_base);
DEBUG("amounts.commodities", "Annotating commodity for amount "
- << *this << std::endl
- << " price " << (tprice ? tprice->to_string() : "NONE") << " "
- << " date " << (tdate ? *tdate : moment_t()) << " "
- << " ttag " << (ttag ? *ttag : "NONE"));
+ << *this << std::endl << details);
if (commodity_t * ann_comm =
- annotated_commodity_t::find_or_create
- (*this_base,
- ! tprice && this_ann ? this_ann->price : tprice,
- ! tdate && this_ann ? this_ann->date : tdate,
- ! ttag && this_ann ? this_ann->tag : ttag))
+ this_base->parent().find_or_create(*this_base, details))
set_commodity(*ann_comm);
+#ifdef ASSERTS_ON
+ else
+ assert(false);
+#endif
DEBUG("amounts.commodities", " Annotated amount is " << *this);
}
@@ -802,69 +768,48 @@ amount_t amount_t::strip_annotations(const bool _keep_price,
<< " keep date " << _keep_date << " "
<< " keep tag " << _keep_tag);
- annotated_commodity_t&
- ann_comm(static_cast<annotated_commodity_t&>(commodity()));
- assert(ann_comm.base);
-
- commodity_t * new_comm;
+ annotated_commodity_t& ann_comm(commodity().as_annotated());
+ commodity_t * new_comm;
- if ((_keep_price && ann_comm.price) ||
- (_keep_date && ann_comm.date) ||
- (_keep_tag && ann_comm.tag))
+ if ((_keep_price && ann_comm.details.price) ||
+ (_keep_date && ann_comm.details.date) ||
+ (_keep_tag && ann_comm.details.tag))
{
- new_comm = annotated_commodity_t::find_or_create
- (*ann_comm.ptr,
- _keep_price ? ann_comm.price : optional<amount_t>(),
- _keep_date ? ann_comm.date : optional<moment_t>(),
- _keep_tag ? ann_comm.tag : optional<string>());
+ new_comm = ann_comm.parent().find_or_create
+ (ann_comm.referent(),
+ annotation_t(_keep_price ? ann_comm.details.price : optional<amount_t>(),
+ _keep_date ? ann_comm.details.date : optional<moment_t>(),
+ _keep_tag ? ann_comm.details.tag : optional<string>()));
} else {
- new_comm = commodity_t::find_or_create(ann_comm.base_symbol());
+ new_comm = ann_comm.parent().find_or_create(ann_comm.base_symbol());
}
assert(new_comm);
amount_t t(*this);
t.set_commodity(*new_comm);
- DEBUG("amounts.commodities", " Reduced amount is " << t);
+
+ DEBUG("amounts.commodities", " Stripped amount is " << t);
return t;
}
-optional<amount_t> amount_t::price() const
+bool amount_t::commodity_annotated() const
{
- if (commodity_ && commodity_->annotated &&
- ((annotated_commodity_t *)commodity_)->price) {
- amount_t t(*((annotated_commodity_t *)commodity_)->price);
- t *= number();
- DEBUG("amounts.commodities",
- "Returning price of " << *this << " = " << t);
- return t;
- }
- return optional<amount_t>();
+ assert(! commodity().annotated || commodity().as_annotated().details);
+ return commodity().annotated;
}
-optional<moment_t> amount_t::date() const
+annotation_t amount_t::annotation_details() const
{
- if (commodity_ && commodity_->annotated) {
- DEBUG("amounts.commodities",
- "Returning date of " << *this << " = "
- << ((annotated_commodity_t *)commodity_)->date);
- return ((annotated_commodity_t *)commodity_)->date;
- }
- return optional<moment_t>();
-}
+ assert(! commodity().annotated || commodity().as_annotated().details);
-optional<string> amount_t::tag() const
-{
- if (commodity_ && commodity_->annotated) {
- DEBUG("amounts.commodities",
- "Returning tag of " << *this << " = "
- << ((annotated_commodity_t *)commodity_)->tag);
- return ((annotated_commodity_t *)commodity_)->tag;
+ if (commodity().annotated) {
+ annotated_commodity_t& ann_comm(commodity().as_annotated());
+ return ann_comm.details;
}
- return optional<string>();
+ return annotation_t();
}
-
static void parse_quantity(std::istream& in, string& value)
{
char buf[256];
@@ -923,16 +868,15 @@ static void parse_commodity(std::istream& in, string& symbol)
symbol = buf;
}
-void parse_annotations(std::istream& in,
- optional<amount_t>& price,
- optional<moment_t>& date,
- optional<string>& tag)
+bool parse_annotations(commodity_pool_t& parent,
+ std::istream& in,
+ annotation_t& details)
{
do {
char buf[256];
char c = peek_next_nonws(in);
if (c == '{') {
- if (price)
+ if (details.price)
throw_(amount_error, "Commodity specifies more than one price");
in.get(c);
@@ -943,7 +887,7 @@ void parse_annotations(std::istream& in,
throw_(amount_error, "Commodity price lacks closing brace");
amount_t temp;
- temp.parse(buf, AMOUNT_PARSE_NO_MIGRATE);
+ temp.parse(parent, buf, AMOUNT_PARSE_NO_MIGRATE);
temp.in_place_reduce();
// Since this price will maintain its own precision, make sure
@@ -954,10 +898,10 @@ void parse_annotations(std::istream& in,
temp.quantity->prec < temp.commodity().precision())
temp = temp.round(); // no need to retain individual precision
- price = temp;
+ details.price = temp;
}
else if (c == '[') {
- if (date)
+ if (details.date)
throw_(amount_error, "Commodity specifies more than one date");
in.get(c);
@@ -967,10 +911,10 @@ void parse_annotations(std::istream& in,
else
throw_(amount_error, "Commodity date lacks closing bracket");
- date = parse_datetime(buf);
+ details.date = parse_datetime(buf);
}
else if (c == '(') {
- if (tag)
+ if (details.tag)
throw_(amount_error, "Commodity specifies more than one tag");
in.get(c);
@@ -980,7 +924,7 @@ void parse_annotations(std::istream& in,
else
throw_(amount_error, "Commodity tag lacks closing parenthesis");
- tag = buf;
+ details.tag = buf;
}
else {
break;
@@ -989,25 +933,28 @@ void parse_annotations(std::istream& in,
DEBUG("amounts.commodities",
"Parsed commodity annotations: "
- << " price " << (price ? price->to_string() : "NONE") << " "
- << " date " << (date ? *date : moment_t()) << " "
- << " tag " << (tag ? *tag : "NONE"));
+ << " price "
+ << (details.price ? details.price->to_string() : "NONE") << " "
+ << " date "
+ << (details.date ? *details.date : moment_t()) << " "
+ << " tag "
+ << (details.tag ? *details.tag : "NONE"));
+
+ return details;
}
-void amount_t::parse(std::istream& in, uint8_t flags)
+void amount_t::parse(commodity_pool_t& parent, std::istream& in, uint8_t flags)
{
// The possible syntax for an amount is:
//
// [-]NUM[ ]SYM [@ AMOUNT]
// SYM[ ][-]NUM [@ AMOUNT]
- string symbol;
- string quant;
- optional<amount_t> tprice;
- optional<moment_t> tdate;
- optional<string> ttag;
- unsigned int comm_flags = COMMODITY_STYLE_DEFAULTS;
- bool negative = false;
+ string symbol;
+ string quant;
+ annotation_t details;
+ unsigned int comm_flags = COMMODITY_STYLE_DEFAULTS;
+ bool negative = false;
char c = peek_next_nonws(in);
if (c == '-') {
@@ -1030,7 +977,7 @@ void amount_t::parse(std::istream& in, uint8_t flags)
comm_flags |= COMMODITY_STYLE_SUFFIXED;
if (! in.eof() && ((n = in.peek()) != '\n'))
- parse_annotations(in, tprice, tdate, ttag);
+ parse_annotations(parent, in, details);
}
} else {
parse_commodity(in, symbol);
@@ -1042,7 +989,7 @@ void amount_t::parse(std::istream& in, uint8_t flags)
parse_quantity(in, quant);
if (! quant.empty() && ! in.eof() && ((n = in.peek()) != '\n'))
- parse_annotations(in, tprice, tdate, ttag);
+ parse_annotations(parent, in, details);
}
}
@@ -1059,16 +1006,15 @@ void amount_t::parse(std::istream& in, uint8_t flags)
if (symbol.empty()) {
commodity_ = NULL;
} else {
- commodity_ = commodity_t::find(symbol);
+ commodity_ = parent.find(symbol);
if (! commodity_) {
- commodity_ = commodity_t::create(symbol);
+ commodity_ = parent.create(symbol);
newly_created = true;
}
assert(commodity_);
- if (tprice || tdate || ttag)
- commodity_ = annotated_commodity_t::find_or_create
- (*commodity_, tprice, tdate, ttag);
+ if (details)
+ commodity_ = parent.find_or_create(*commodity_, details);
}
// Determine the precision of the amount, based on the usage of
@@ -1100,7 +1046,8 @@ void amount_t::parse(std::istream& in, uint8_t flags)
// Set the commodity's flags and precision accordingly
- if (commodity_ && (newly_created || ! (flags & AMOUNT_PARSE_NO_MIGRATE))) {
+ if (commodity_ &&
+ (newly_created || ! (flags & AMOUNT_PARSE_NO_MIGRATE))) {
commodity().add_flags(comm_flags);
if (quantity->prec > commodity().precision())
commodity().set_precision(quantity->prec);
@@ -1138,13 +1085,14 @@ void amount_t::parse(std::istream& in, uint8_t flags)
in_place_reduce();
}
-void amount_t::parse_conversion(const string& larger_str,
+void amount_t::parse_conversion(commodity_pool_t& parent,
+ const string& larger_str,
const string& smaller_str)
{
amount_t larger, smaller;
- larger.parse(larger_str, AMOUNT_PARSE_NO_REDUCE);
- smaller.parse(smaller_str, AMOUNT_PARSE_NO_REDUCE);
+ larger.parse(parent, larger_str, AMOUNT_PARSE_NO_REDUCE);
+ smaller.parse(parent, smaller_str, AMOUNT_PARSE_NO_REDUCE);
larger *= smaller.number();
@@ -1323,7 +1271,7 @@ void amount_t::print(std::ostream& _out, bool omit_commodity,
if (! omit_commodity && comm.annotated) {
annotated_commodity_t& ann(static_cast<annotated_commodity_t&>(comm));
- assert(&*ann.price != this);
+ assert(&*ann.details.price != this);
ann.write_annotations(out);
}
@@ -1337,30 +1285,34 @@ void amount_t::print(std::ostream& _out, bool omit_commodity,
}
-void amount_t::read(std::istream& in)
+void amount_t::read(commodity_pool_t& parent, std::istream& in)
{
commodity_t::ident_t ident;
read_binary_long(in, ident);
if (ident == 0xffffffff)
commodity_ = NULL;
else if (ident == 0)
- commodity_ = commodity_t::null_commodity;
- else
- commodity_ = (*commodity_t::commodities_by_ident)[ident - 1];
+ commodity_ = parent.null_commodity;
+ else {
+ commodity_ = parent.find(ident - 1);
+ assert(commodity_);
+ }
read_quantity(in);
}
-void amount_t::read(char *& data)
+void amount_t::read(commodity_pool_t& parent, char *& data)
{
commodity_t::ident_t ident;
read_binary_long(data, ident);
if (ident == 0xffffffff)
commodity_ = NULL;
else if (ident == 0)
- commodity_ = commodity_t::null_commodity;
- else
- commodity_ = (*commodity_t::commodities_by_ident)[ident - 1];
+ commodity_ = parent.null_commodity;
+ else {
+ commodity_ = parent.find(ident - 1);
+ assert(commodity_);
+ }
read_quantity(data);
}
diff --git a/src/amount.h b/src/amount.h
index 0c9501bc..280613d7 100644
--- a/src/amount.h
+++ b/src/amount.h
@@ -52,6 +52,8 @@
namespace ledger {
class commodity_t;
+class annotation_t;
+class commodity_pool_t;
DECLARE_EXCEPTION(amount_error);
@@ -88,6 +90,12 @@ public:
static void shutdown();
/**
+ * The default_pool is a static variable indicating which commodity
+ * pool should be used when none is specified.
+ */
+ static commodity_pool_t * default_pool;
+
+ /**
* The `keep_base' member determines whether scalable commodities
* are automatically converted to their most reduced form when
* printing. The default is true.
@@ -291,11 +299,12 @@ public:
* compact form greater than 1.0. That is, 3599s will unreduce to
* 59.98m, while 3601 unreduces to 1h.
*
- * value(moment_t) returns the history value of an amount, based on
- * the price history of its commodity. For example, if the amount
- * were 10 AAPL, and on Apr 10, 2000 each share of AAPL was worth
- * $10, then call value() for that moment in time would yield the
- * amount $100.00.
+ * value(optional<moment_t>) returns the historical value for an
+ * amount -- the default moment returns the most recently known
+ * price -- based on the price history of its commodity. For
+ * example, if the amount were 10 AAPL, and on Apr 10, 2000 each
+ * share of AAPL was worth $10, then call value() for that moment in
+ * time would yield the amount $100.00.
*
* Further, for the sake of efficiency and avoiding temporary
* objects, the following methods support "in-place" variants that
@@ -341,7 +350,8 @@ public:
}
amount_t& in_place_unreduce();
- amount_t value(const moment_t& moment) const;
+ optional<amount_t> value(const optional<moment_t>& moment =
+ optional<moment_t>()) const;
/**
* Truth tests. An amount may be truth test in several ways:
@@ -466,38 +476,26 @@ public:
* the price argument is required, although it can be passed as
* `optional<amount_t>()' if no price is desired.
*
+ * commodity_annotated() returns true if an amount's commodity has
+ * any annotation details associated with it.
+ *
+ * annotation_details() returns all of the details of an annotated
+ * commodity's annotations. The structure returns will evaluate as
+ * boolean false if there are no details.
+ *
* strip_annotations([keep_price, keep_date, keep_tag]) returns an
* amount whose commodity's annotations have been stripped. The
* three `keep_' arguments determine which annotation detailed are
* kept, meaning that the default is to follow whatever
* amount_t::keep_price, amount_t::keep_date and amount_t::keep_tag
* have been set to (which all default to false).
- *
- * price() returns an amount's annotated commodity's price. This
- * return value is of type `optional<amount_t>', so it must be
- * tested for boolean truth to determine if an annotated price even
- * existed.
- *
- * date() returns an amount's annotated commodity's date. This
- * return value is of type `optional<moment_t>'.
- *
- * tag() returns an amount's annotated commodity's tag. This return
- * value is of type `optional<string>'.
*/
- void annotate_commodity(const optional<amount_t>& tprice,
- const optional<moment_t>& tdate = optional<moment_t>(),
- const optional<string>& ttag = optional<string>());
-
- amount_t strip_annotations(const bool _keep_price = keep_price,
- const bool _keep_date = keep_date,
- const bool _keep_tag = keep_tag) const;
-
- optional<amount_t> price() const;
- optional<moment_t> date() const;
- optional<string> tag() const;
-
-#define AMOUNT_PARSE_NO_MIGRATE 0x01
-#define AMOUNT_PARSE_NO_REDUCE 0x02
+ void annotate_commodity(const annotation_t& details);
+ bool commodity_annotated() const;
+ annotation_t annotation_details() const;
+ amount_t strip_annotations(const bool _keep_price = keep_price,
+ const bool _keep_date = keep_date,
+ const bool _keep_tag = keep_tag) const;
/**
* Parsing methods. The method `parse' is used to parse an amount
@@ -505,11 +503,18 @@ public:
* defined which simply calls parse on the input stream. The
* `parse' method has two forms:
*
- * parse(istream, unsigned char flags) parses an amount from the
- * given input stream.
+ * parse(commodity_pool_t, istream, flags_t) parses an
+ * amount from the given input stream, registering commodity details
+ * according to the commodity pool which is passed in as the first
+ * parameter.
+ *
+ * parse(istream, flags_t) is the same as the preceding function,
+ * only it uses `amount_t::default_pool' as the commodity pool.
*
- * parse(string, unsigned char flags) parses an amount from the
- * given string.
+ * parse(commodity_pool_t, string, flags_t) parses an amount from
+ * the given string.
+ *
+ * parse(string, flags_t) also parses an amount from a string.
*
* The `flags' argument of both parsing may be one or more of the
* following:
@@ -538,15 +543,36 @@ public:
* amount_t::parse_conversion("1.0m", "60s"); // a minute is 60 seconds
* amount_t::parse_conversion("1.0h", "60m"); // an hour is 60 minutes
*/
- void parse(std::istream& in, unsigned char flags = 0);
- void parse(const string& str, unsigned char flags = 0) {
+#define AMOUNT_PARSE_NO_MIGRATE 0x01
+#define AMOUNT_PARSE_NO_REDUCE 0x02
+
+ typedef uint_least8_t flags_t;
+
+ void parse(commodity_pool_t& parent, std::istream& in, flags_t flags = 0);
+ void parse(commodity_pool_t& parent, const string& str, flags_t flags = 0) {
std::istringstream stream(str);
- parse(stream, flags);
+ parse(parent, stream, flags);
}
- static void parse_conversion(const string& larger_str,
+ void parse(std::istream& in, flags_t flags = 0) {
+ assert(default_pool);
+ parse(*default_pool, in, flags);
+ }
+ void parse(const string& str, flags_t flags = 0) {
+ assert(default_pool);
+ parse(*default_pool, str, flags);
+ }
+
+ static void parse_conversion(commodity_pool_t& parent,
+ const string& larger_str,
const string& smaller_str);
+ static void parse_conversion(const string& larger_str,
+ const string& smaller_str) {
+ assert(default_pool);
+ parse_conversion(*default_pool, larger_str, smaller_str);
+ }
+
/**
* Printing methods. An amount may be output to a stream using the
* `print' method. There is also a global operator<< defined which
@@ -571,18 +597,40 @@ public:
* input stream or a character pointer, and it may be serialized to
* an output stream. The methods used are:
*
- * read(istream) reads an amount from the given input stream. It
- * must have been put there using `write(ostream)'.
+ * read(commodity_pool_t, istream) reads an amount from the given
+ * input stream. It must have been put there using
+ * `write(ostream)'. Also, the given pool must be exactly what it
+ * was at the time the amount was `written'. Thus, the required
+ * flow of logic is:
+ * amount.write(out)
+ * pool.write(out)
+ * pool.read(in)
+ * amount.read(pool, in)
+ *
+ * read(istream) does the same as read, only it relies on
+ * `amount_t::default_pool' to specify the pool.
+ *
+ * read(commodity_pool_t, char *&) reads an amount from data which
+ * has been read from an input stream into a buffer. it advances
+ * the pointer passed in to the end of the deserialized amount.
*
- * read(char *&) reads an amount from data which has been read from
- * an input stream into a buffer. it advances the pointer passed in
- * to the end of the deserialized amount.
+ * read(char *&) does the same as read, only it relies on
+ * `amount_t::default_pool' to specify the pool.
*
* write(ostream) writes an amount to an output stream in a compact
* binary format.
*/
- void read(std::istream& in);
- void read(char *& data);
+ void read(commodity_pool_t& parent, std::istream& in);
+ void read(commodity_pool_t& parent, char *& data);
+
+ void read(std::istream& in) {
+ assert(default_pool);
+ read(*default_pool, in);
+ }
+ void read(char *& data) {
+ assert(default_pool);
+ read(*default_pool, data);
+ }
void write(std::ostream& out) const;
@@ -614,10 +662,9 @@ public:
bool valid() const;
private:
- friend void parse_annotations(std::istream& in,
- optional<amount_t>& price,
- optional<moment_t>& date,
- optional<string>& tag);
+ friend bool parse_annotations(commodity_pool_t& parent,
+ std::istream& in,
+ annotation_t& details);
};
inline amount_t amount_t::exact(const string& value) {
@@ -672,11 +719,12 @@ inline amount_t amount_t::round() const {
}
inline bool amount_t::has_commodity() const {
- return commodity_ && commodity_ != commodity_t::null_commodity;
+ return commodity_ && commodity_ != commodity_->parent().null_commodity;
}
inline commodity_t& amount_t::commodity() const {
- return has_commodity() ? *commodity_ : *commodity_t::null_commodity;
+ // jww (2007-05-02): Should be a way to access null_commodity better
+ return has_commodity() ? *commodity_ : *default_pool->null_commodity;
}
} // namespace ledger
diff --git a/src/balance.cc b/src/balance.cc
index 1baad2bf..dd822963 100644
--- a/src/balance.cc
+++ b/src/balance.cc
@@ -133,7 +133,8 @@ balance_t& balance_t::operator/=(const amount_t& amt)
return *this;
}
-amount_t balance_t::amount(const commodity_t& commodity) const
+optional<amount_t>
+balance_t::amount(const optional<const commodity_t&>& commodity) const
{
if (! commodity) {
if (amounts.size() == 1) {
@@ -151,71 +152,27 @@ amount_t balance_t::amount(const commodity_t& commodity) const
}
}
else if (amounts.size() > 0) {
- amounts_map::const_iterator i = amounts.find(&commodity);
+ amounts_map::const_iterator i = amounts.find(&*commodity);
if (i != amounts.end())
return (*i).second;
}
- return amount_t();
+ return optional<amount_t>();
}
-balance_t balance_t::value(const moment_t& moment) const
-{
- balance_t temp;
-
- for (amounts_map::const_iterator i = amounts.begin();
- i != amounts.end();
- i++)
- temp += (*i).second.value(moment);
-
- return temp;
-}
-
-optional<balance_t> balance_t::price() const
+optional<balance_t>
+balance_t::value(const optional<moment_t>& moment) const
{
optional<balance_t> temp;
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
- i++) {
- optional<amount_t> i_price = (*i).second.price();
- if (i_price) {
+ i++)
+ if (optional<amount_t> val = (*i).second.value(moment)) {
if (! temp)
temp = balance_t();
- *temp += *i_price;
+ *temp += *val;
}
- }
- return temp;
-}
-optional<moment_t> balance_t::date() const
-{
- optional<moment_t> temp;
-
- for (amounts_map::const_iterator i = amounts.begin();
- i != amounts.end();
- i++) {
- optional<moment_t> tdate = (*i).second.date();
- if (! temp && tdate)
- temp = *tdate;
- else if (temp && tdate && temp != tdate)
- return optional<moment_t>();
- }
- return temp;
-}
-
-optional<string> balance_t::tag() const
-{
- optional<string> temp;
-
- for (amounts_map::const_iterator i = amounts.begin();
- i != amounts.end();
- i++) {
- optional<string> ttag = (*i).second.tag();
- if (! temp && ttag)
- temp = *ttag;
- else if (temp && ttag && temp != ttag)
- return optional<string>();
- }
return temp;
}
@@ -243,52 +200,33 @@ void balance_t::write(std::ostream& out,
if (lwidth == -1)
lwidth = first_width;
- if (commodity_t::commodities_sorted) {
- for (amounts_map::const_iterator i = amounts.begin();
- i != amounts.end();
- i++) {
- int width;
- if (! first) {
- out << std::endl;
- width = lwidth;
- } else {
- first = false;
- width = first_width;
- }
+ typedef std::vector<const amount_t *> amounts_array;
+ amounts_array sorted;
- out.width(width);
- out.fill(' ');
- out << std::right << (*i).second;
- }
- } else {
- typedef std::vector<const amount_t *> amounts_array;
- amounts_array sorted;
-
- for (amounts_map::const_iterator i = amounts.begin();
- i != amounts.end();
- i++)
- if ((*i).second)
- sorted.push_back(&(*i).second);
+ for (amounts_map::const_iterator i = amounts.begin();
+ i != amounts.end();
+ i++)
+ if ((*i).second)
+ sorted.push_back(&(*i).second);
- std::stable_sort(sorted.begin(), sorted.end(),
- compare_amount_commodities());
+ std::stable_sort(sorted.begin(), sorted.end(),
+ compare_amount_commodities());
- for (amounts_array::const_iterator i = sorted.begin();
- i != sorted.end();
- i++) {
- int width;
- if (! first) {
- out << std::endl;
- width = lwidth;
- } else {
- first = false;
- width = first_width;
- }
-
- out.width(width);
- out.fill(' ');
- out << std::right << **i;
+ for (amounts_array::const_iterator i = sorted.begin();
+ i != sorted.end();
+ i++) {
+ int width;
+ if (! first) {
+ out << std::endl;
+ width = lwidth;
+ } else {
+ first = false;
+ width = first_width;
}
+
+ out.width(width);
+ out.fill(' ');
+ out << std::right << **i;
}
if (first) {
diff --git a/src/balance.h b/src/balance.h
index 38693bda..62f9ba86 100644
--- a/src/balance.h
+++ b/src/balance.h
@@ -93,15 +93,19 @@ public:
bool operator<(const balance_t& bal) const {
for (amounts_map::const_iterator i = bal.amounts.begin();
i != bal.amounts.end();
- i++)
- if (! (amount(*(*i).first) < (*i).second))
+ i++) {
+ optional<amount_t> amt = amount(*(*i).first);
+ if (amt && ! (*amt < (*i).second))
return false;
+ }
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
- i++)
- if (! ((*i).second < bal.amount(*(*i).first)))
+ i++) {
+ optional<amount_t> amt = bal.amount(*(*i).first);
+ if (amt && ! ((*i).second < *amt))
return false;
+ }
if (bal.amounts.size() == 0 && amounts.size() == 0)
return false;
@@ -146,13 +150,10 @@ public:
return true;
}
- amount_t amount(const commodity_t& commodity =
- *commodity_t::null_commodity) const;
- balance_t value(const moment_t& moment = now) const;
-
- optional<balance_t> price() const;
- optional<moment_t> date() const;
- optional<string> tag() const;
+ optional<amount_t> amount(const optional<const commodity_t&>& commodity =
+ optional<const commodity_t&>()) const;
+ optional<balance_t> value(const optional<moment_t>& moment =
+ optional<moment_t>()) const;
balance_t
strip_annotations(const bool keep_price = amount_t::keep_price,
@@ -335,24 +336,15 @@ public:
return temp;
}
- amount_t amount(const commodity_t& commodity =
- *commodity_t::null_commodity) const {
+ optional<amount_t> amount(const optional<const commodity_t&>& commodity =
+ optional<const commodity_t&>()) const {
return quantity.amount(commodity);
}
- balance_t value(const moment_t& moment = now) const {
+ optional<balance_t> value(const optional<moment_t>& moment =
+ optional<moment_t>()) const {
return quantity.value(moment);
}
- optional<balance_t> price() const {
- return quantity.price();
- }
- optional<moment_t> date() const {
- return quantity.date();
- }
- optional<string> tag() const {
- return quantity.tag();
- }
-
balance_t
strip_annotations(const bool keep_price = amount_t::keep_price,
const bool keep_date = amount_t::keep_date,
diff --git a/src/commodity.cc b/src/commodity.cc
index dba1eb98..1fac4a4e 100644
--- a/src/commodity.cc
+++ b/src/commodity.cc
@@ -43,61 +43,98 @@
namespace ledger {
-#ifndef THREADSAFE
-base_commodities_map commodity_base_t::commodities;
-
-commodity_base_t::updater_t * commodity_base_t::updater = NULL;
-
-commodities_map commodity_t::commodities;
-commodities_array * commodity_t::commodities_by_ident;
-bool commodity_t::commodities_sorted = false;
-commodity_t * commodity_t::null_commodity;
-commodity_t * commodity_t::default_commodity = NULL;
-#endif
-
-void commodity_base_t::add_price(const moment_t& date,
- const amount_t& price)
+void commodity_t::add_price(const moment_t& date,
+ const amount_t& price)
{
- if (! history)
- history = history_t();
+ if (! base->history)
+ base->history = history_t();
- history_map::iterator i = history->prices.find(date);
- if (i != history->prices.end()) {
+ history_map::iterator i = base->history->prices.find(date);
+ if (i != base->history->prices.end()) {
(*i).second = price;
} else {
std::pair<history_map::iterator, bool> result
- = history->prices.insert(history_pair(date, price));
+ = base->history->prices.insert(history_pair(date, price));
assert(result.second);
}
}
-bool commodity_base_t::remove_price(const moment_t& date)
+bool commodity_t::remove_price(const moment_t& date)
{
- if (history) {
- history_map::size_type n = history->prices.erase(date);
+ if (base->history) {
+ history_map::size_type n = base->history->prices.erase(date);
if (n > 0) {
- if (history->prices.empty())
- history.reset();
+ if (base->history->prices.empty())
+ base->history.reset();
return true;
}
}
return false;
}
-commodity_base_t * commodity_base_t::create(const string& symbol)
+optional<amount_t> commodity_t::value(const optional<moment_t>& moment)
{
- commodity_base_t * commodity = new commodity_base_t(symbol);
+ optional<moment_t> age;
+ optional<amount_t> price;
- DEBUG("amounts.commodities", "Creating base commodity " << symbol);
+ if (base->history) {
+ assert(base->history->prices.size() > 0);
+
+ if (! moment) {
+ history_map::reverse_iterator r = base->history->prices.rbegin();
+ age = (*r).first;
+ price = (*r).second;
+ } else {
+ history_map::iterator i = base->history->prices.lower_bound(*moment);
+ if (i == base->history->prices.end()) {
+ history_map::reverse_iterator r = base->history->prices.rbegin();
+ age = (*r).first;
+ price = (*r).second;
+ } else {
+ age = (*i).first;
+ if (*moment != *age) {
+ if (i != base->history->prices.begin()) {
+ --i;
+ age = (*i).first;
+ price = (*i).second;
+ } else {
+ age = optional<moment_t>();
+ }
+ } else {
+ price = (*i).second;
+ }
+ }
+ }
+ }
+
+ if (! (flags() & COMMODITY_STYLE_NOMARKET)) {
+ if (optional<amount_t> quote = parent().get_quote
+ (*this, age, moment,
+ (base->history && base->history->prices.size() > 0 ?
+ (*base->history->prices.rbegin()).first : optional<moment_t>())))
+ return *quote;
+ }
+ return price;
+}
+
+commodity_t::operator bool() const
+{
+ return this != parent().null_commodity;
+}
- std::pair<base_commodities_map::iterator, bool> result
- = commodities.insert(base_commodities_pair(symbol, commodity));
- assert(result.second);
+annotated_commodity_t& commodity_t::as_annotated()
+{
+ assert(annotated);
+ return *polymorphic_downcast<annotated_commodity_t *>(this);
+}
- return commodity;
+const annotated_commodity_t& commodity_t::as_annotated() const
+{
+ assert(annotated);
+ return *polymorphic_downcast<const annotated_commodity_t *>(this);
}
-bool commodity_t::needs_quotes(const string& symbol)
+bool commodity_t::symbol_needs_quotes(const string& symbol)
{
for (const char * p = symbol.c_str(); *p; p++)
if (std::isspace(*p) || std::isdigit(*p) || *p == '-' || *p == '.')
@@ -108,7 +145,7 @@ bool commodity_t::needs_quotes(const string& symbol)
bool commodity_t::valid() const
{
- if (symbol().empty() && this != null_commodity) {
+ if (symbol().empty() && this != parent().null_commodity) {
DEBUG("ledger.validate",
"commodity_t: symbol().empty() && this != null_commodity");
return false;
@@ -127,204 +164,225 @@ bool commodity_t::valid() const
return true;
}
-commodity_t * commodity_t::create(const string& symbol)
+bool annotated_commodity_t::operator==(const commodity_t& comm) const
{
- std::auto_ptr<commodity_t> commodity(new commodity_t);
-
- commodity->base = commodity_base_t::create(symbol);
-
- if (needs_quotes(symbol)) {
- commodity->qualified_symbol = "\"";
- commodity->qualified_symbol += symbol;
- commodity->qualified_symbol += "\"";
- } else {
- commodity->qualified_symbol = symbol;
- }
-
- DEBUG("amounts.commodities",
- "Creating commodity " << commodity->qualified_symbol);
-
- std::pair<commodities_map::iterator, bool> result
- = commodities.insert(commodities_pair(symbol, commodity.get()));
- if (! result.second)
- return NULL;
+ // If the base commodities don't match, the game's up.
+ if (base != comm.base)
+ return false;
- commodity->ident = commodities_by_ident->size();
- commodities_by_ident->push_back(commodity.get());
+ assert(annotated);
+ if (! comm.annotated)
+ return false;
- // Start out the new commodity with the default commodity's flags
- // and precision, if one has been defined.
- if (default_commodity)
- commodity->drop_flags(COMMODITY_STYLE_THOUSANDS |
- COMMODITY_STYLE_NOMARKET);
+ if (details != comm.as_annotated().details)
+ return false;
- return commodity.release();
+ return true;
}
-commodity_t * commodity_t::find_or_create(const string& symbol)
+void
+annotated_commodity_t::write_annotations(std::ostream& out,
+ const annotation_t& info)
{
- DEBUG("amounts.commodities", "Find-or-create commodity " << symbol);
+ if (info.price)
+ out << " {" << *info.price << '}';
- commodity_t * commodity = find(symbol);
- if (commodity)
- return commodity;
- return create(symbol);
+ if (info.date)
+ out << " [" << *info.date << ']';
+
+ if (info.tag)
+ out << " (" << *info.tag << ')';
}
-commodity_t * commodity_t::find(const string& symbol)
+bool compare_amount_commodities::operator()(const amount_t * left,
+ const amount_t * right) const
{
- DEBUG("amounts.commodities", "Find commodity " << symbol);
+ commodity_t& leftcomm(left->commodity());
+ commodity_t& rightcomm(right->commodity());
- commodities_map::const_iterator i = commodities.find(symbol);
- if (i != commodities.end())
- return (*i).second;
- return NULL;
-}
+ int cmp = leftcomm.base_symbol().compare(rightcomm.base_symbol());
+ if (cmp != 0)
+ return cmp < 0;
-amount_t commodity_base_t::value(const moment_t& moment)
-{
- moment_t age;
- amount_t price;
+ if (! leftcomm.annotated) {
+ assert(rightcomm.annotated);
+ return true;
+ }
+ else if (! rightcomm.annotated) {
+ assert(leftcomm.annotated);
+ return false;
+ }
+ else {
+ annotated_commodity_t& aleftcomm(static_cast<annotated_commodity_t&>(leftcomm));
+ annotated_commodity_t& arightcomm(static_cast<annotated_commodity_t&>(rightcomm));
- if (history) {
- assert(history->prices.size() > 0);
+ if (! aleftcomm.details.price && arightcomm.details.price)
+ return true;
+ if (aleftcomm.details.price && ! arightcomm.details.price)
+ return false;
- if (! is_valid_moment(moment)) {
- history_map::reverse_iterator r = history->prices.rbegin();
- age = (*r).first;
- price = (*r).second;
- } else {
- history_map::iterator i = history->prices.lower_bound(moment);
- if (i == history->prices.end()) {
- history_map::reverse_iterator r = history->prices.rbegin();
- age = (*r).first;
- price = (*r).second;
+ if (aleftcomm.details.price && arightcomm.details.price) {
+ amount_t leftprice(*aleftcomm.details.price);
+ leftprice.in_place_reduce();
+ amount_t rightprice(*arightcomm.details.price);
+ rightprice.in_place_reduce();
+
+ if (leftprice.commodity() == rightprice.commodity()) {
+ return (leftprice - rightprice).sign() < 0;
} else {
- age = (*i).first;
- if (moment != age) {
- if (i != history->prices.begin()) {
- --i;
- age = (*i).first;
- price = (*i).second;
- } else {
- age = moment_t();
- }
- } else {
- price = (*i).second;
- }
+ // Since we have two different amounts, there's really no way
+ // to establish a true sorting order; we'll just do it based
+ // on the numerical values.
+ leftprice.clear_commodity();
+ rightprice.clear_commodity();
+ return (leftprice - rightprice).sign() < 0;
}
}
+
+ if (! aleftcomm.details.date && arightcomm.details.date)
+ return true;
+ if (aleftcomm.details.date && ! arightcomm.details.date)
+ return false;
+
+ if (aleftcomm.details.date && arightcomm.details.date) {
+ duration_t diff = *aleftcomm.details.date - *arightcomm.details.date;
+ return diff.is_negative();
+ }
+
+ if (! aleftcomm.details.tag && arightcomm.details.tag)
+ return true;
+ if (aleftcomm.details.tag && ! arightcomm.details.tag)
+ return false;
+
+ if (aleftcomm.details.tag && arightcomm.details.tag)
+ return *aleftcomm.details.tag < *arightcomm.details.tag;
+
+ assert(false);
+ return true;
}
+}
- if (updater && ! (flags & COMMODITY_STYLE_NOMARKET))
- (*updater)(*this, moment, age,
- (history && history->prices.size() > 0 ?
- (*history->prices.rbegin()).first : moment_t()), price);
+commodity_pool_t::commodity_pool_t() : default_commodity(NULL)
+{
+ null_commodity = create("");
+ null_commodity->add_flags(COMMODITY_STYLE_NOMARKET |
+ COMMODITY_STYLE_BUILTIN);
- return price;
+ // Add time commodity conversions, so that timelog's may be parsed
+ // in terms of seconds, but reported as minutes or hours.
+ commodity_t * commodity = create("s");
+ commodity->add_flags(COMMODITY_STYLE_NOMARKET | COMMODITY_STYLE_BUILTIN);
+
+ amount_t::parse_conversion(*this, "1.0m", "60s");
+ amount_t::parse_conversion(*this, "1.0h", "60m");
}
-bool annotated_commodity_t::operator==(const commodity_t& comm) const
+commodity_t * commodity_pool_t::create(const string& symbol)
{
- // If the base commodities don't match, the game's up.
- if (base != comm.base)
- return false;
+ shared_ptr<commodity_base_t> base_commodity(new commodity_base_t(symbol));
+ std::auto_ptr<commodity_t> commodity(new commodity_t(this, base_commodity));
- if (price &&
- (! comm.annotated ||
- price != static_cast<const annotated_commodity_t&>(comm).price))
- return false;
+ DEBUG("amounts.commodities", "Creating base commodity " << symbol);
- if (date &&
- (! comm.annotated ||
- date != static_cast<const annotated_commodity_t&>(comm).date))
- return false;
+ // Create the "qualified symbol" version of this commodity's symbol
+ if (commodity_t::symbol_needs_quotes(symbol)) {
+ commodity->qualified_symbol = "\"";
+ *commodity->qualified_symbol += symbol;
+ *commodity->qualified_symbol += "\"";
+ }
- if (tag &&
- (! comm.annotated ||
- tag != static_cast<const annotated_commodity_t&>(comm).tag))
- return false;
+ DEBUG("amounts.commodities",
+ "Creating commodity '" << commodity->symbol() << "'");
- return true;
+ // Start out the new commodity with the default commodity's flags
+ // and precision, if one has been defined.
+#if 0
+ // jww (2007-05-02): This doesn't do anything currently!
+ if (default_commodity)
+ commodity->drop_flags(COMMODITY_STYLE_THOUSANDS |
+ COMMODITY_STYLE_NOMARKET);
+#endif
+
+ commodity->ident = commodities.size();
+
+ std::pair<commodities_t::iterator, bool> result =
+ commodities.insert(commodity.get());
+ if (! result.second) {
+ assert(false);
+ return NULL;
+ } else {
+ return commodity.release();
+ }
}
-void
-annotated_commodity_t::write_annotations(std::ostream& out,
- const optional<amount_t>& price,
- const optional<moment_t>& date,
- const optional<string>& tag)
+commodity_t * commodity_pool_t::find_or_create(const string& symbol)
{
- if (price)
- out << " {" << *price << '}';
-
- if (date)
- out << " [" << *date << ']';
+ DEBUG("amounts.commodities", "Find-or-create commodity " << symbol);
- if (tag)
- out << " (" << *tag << ')';
+ commodity_t * commodity = find(symbol);
+ if (commodity)
+ return commodity;
+ return create(symbol);
}
-commodity_t *
-annotated_commodity_t::create(const commodity_t& comm,
- const optional<amount_t>& price,
- const optional<moment_t>& date,
- const optional<string>& tag,
- const string& mapping_key)
+commodity_t * commodity_pool_t::find(const string& symbol)
{
- std::auto_ptr<annotated_commodity_t> commodity(new annotated_commodity_t);
+ DEBUG("amounts.commodities", "Find commodity " << symbol);
- // Set the annotated bits
- commodity->price = price;
- commodity->date = date;
- commodity->tag = tag;
+ typedef commodity_pool_t::commodities_t::nth_index<1>::type
+ commodities_by_name;
- commodity->ptr = &comm;
- assert(commodity->ptr);
- commodity->base = comm.base;
- assert(commodity->base);
+ commodities_by_name& name_index = commodities.get<1>();
+ commodities_by_name::const_iterator i = name_index.find(symbol);
+ if (i != name_index.end())
+ return *i;
+ else
+ return NULL;
+}
- commodity->qualified_symbol = comm.symbol();
+commodity_t * commodity_pool_t::find(const commodity_t::ident_t ident)
+{
+ DEBUG("amounts.commodities", "Find commodity by ident " << ident);
- DEBUG("amounts.commodities", "Creating annotated commodity "
- << "symbol " << commodity->symbol()
- << " key " << mapping_key << std::endl
- << " price " << (price ? price->to_string() : "NONE") << " "
- << " date " << (date ? *date : moment_t()) << " "
- << " tag " << (tag ? *tag : "NONE"));
+ typedef commodity_pool_t::commodities_t::nth_index<0>::type
+ commodities_by_ident;
- // Add the fully annotated name to the map, so that this symbol may
- // quickly be found again.
- std::pair<commodities_map::iterator, bool> result
- = commodities.insert(commodities_pair(mapping_key, commodity.get()));
- if (! result.second)
+ commodities_by_ident& ident_index = commodities.get<0>();
+ commodities_by_ident::iterator i = ident_index.find(ident);
+ if (i != ident_index.end())
+ return *i;
+ else
return NULL;
+}
- commodity->ident = commodities_by_ident->size();
- commodities_by_ident->push_back(commodity.get());
+commodity_t *
+commodity_pool_t::create(const string& symbol, const annotation_t& details)
+{
+ commodity_t * new_comm = create(symbol);
+ if (! new_comm)
+ return NULL;
- return commodity.release();
+ if (details)
+ return find_or_create(*new_comm, details);
+ else
+ return new_comm;
}
namespace {
- string make_qualified_name(const commodity_t& comm,
- const optional<amount_t>& price,
- const optional<moment_t>& date,
- const optional<string>& tag)
+ string make_qualified_name(const commodity_t& comm,
+ const annotation_t& details)
{
- if (price && price->sign() < 0)
+ assert(details);
+
+ if (details.price && details.price->sign() < 0)
throw_(amount_error, "A commodity's price may not be negative");
std::ostringstream name;
-
comm.write(name);
- annotated_commodity_t::write_annotations(name, price, date, tag);
+ annotated_commodity_t::write_annotations(name, details);
DEBUG("amounts.commodities", "make_qualified_name for "
- << comm.qualified_symbol << std::endl
- << " price " << (price ? price->to_string() : "NONE") << " "
- << " date " << (date ? *date : moment_t()) << " "
- << " tag " << (tag ? *tag : "NONE"));
-
+ << comm.qualified_symbol << std::endl << details);
DEBUG("amounts.commodities", "qualified_name is " << name.str());
return name.str();
@@ -332,87 +390,88 @@ namespace {
}
commodity_t *
-annotated_commodity_t::find_or_create(const commodity_t& comm,
- const optional<amount_t>& price,
- const optional<moment_t>& date,
- const optional<string>& tag)
+commodity_pool_t::find(const string& symbol, const annotation_t& details)
{
- string name = make_qualified_name(comm, price, date, tag);
+ commodity_t * comm = find(symbol);
+ if (! comm)
+ return NULL;
- commodity_t * ann_comm = commodity_t::find(name);
- if (ann_comm) {
- assert(ann_comm->annotated);
- return ann_comm;
+ if (details) {
+ string name = make_qualified_name(*comm, details);
+
+ if (commodity_t * ann_comm = find(name)) {
+ assert(ann_comm->annotated &&
+ ann_comm->as_annotated().details);
+ return ann_comm;
+ }
+ return NULL;
+ } else {
+ return comm;
}
- return create(comm, price, date, tag, name);
}
-bool compare_amount_commodities::operator()(const amount_t * left,
- const amount_t * right) const
+commodity_t *
+commodity_pool_t::find_or_create(const string& symbol,
+ const annotation_t& details)
{
- commodity_t& leftcomm(left->commodity());
- commodity_t& rightcomm(right->commodity());
+ commodity_t * comm = find(symbol);
+ if (! comm)
+ return NULL;
- int cmp = leftcomm.base_symbol().compare(rightcomm.base_symbol());
- if (cmp != 0)
- return cmp < 0;
+ if (details)
+ return find_or_create(*comm, details);
+ else
+ return comm;
+}
- if (! leftcomm.annotated) {
- assert(rightcomm.annotated);
- return true;
- }
- else if (! rightcomm.annotated) {
- assert(leftcomm.annotated);
- return false;
- }
- else {
- annotated_commodity_t& aleftcomm(static_cast<annotated_commodity_t&>(leftcomm));
- annotated_commodity_t& arightcomm(static_cast<annotated_commodity_t&>(rightcomm));
+commodity_t *
+commodity_pool_t::create(commodity_t& comm,
+ const annotation_t& details,
+ const string& mapping_key)
+{
+ assert(comm);
+ assert(details);
+ assert(! mapping_key.empty());
- if (! aleftcomm.price && arightcomm.price)
- return true;
- if (aleftcomm.price && ! arightcomm.price)
- return false;
+ std::auto_ptr<commodity_t> commodity
+ (new annotated_commodity_t(&comm, details));
- if (aleftcomm.price && arightcomm.price) {
- amount_t leftprice(*aleftcomm.price);
- leftprice.in_place_reduce();
- amount_t rightprice(*arightcomm.price);
- rightprice.in_place_reduce();
+ commodity->qualified_symbol = comm.symbol();
+ assert(! commodity->qualified_symbol->empty());
- if (leftprice.commodity() == rightprice.commodity()) {
- return (leftprice - rightprice).sign() < 0;
- } else {
- // Since we have two different amounts, there's really no way
- // to establish a true sorting order; we'll just do it based
- // on the numerical values.
- leftprice.clear_commodity();
- rightprice.clear_commodity();
- return (leftprice - rightprice).sign() < 0;
- }
- }
+ DEBUG("amounts.commodities", "Creating annotated commodity "
+ << "symbol " << commodity->symbol()
+ << " key " << mapping_key << std::endl << details);
- if (! aleftcomm.date && arightcomm.date)
- return true;
- if (aleftcomm.date && ! arightcomm.date)
- return false;
+ // Add the fully annotated name to the map, so that this symbol may
+ // quickly be found again.
+ commodity->ident = commodities.size();
+ commodity->mapping_key_ = mapping_key;
- if (aleftcomm.date && arightcomm.date) {
- duration_t diff = *aleftcomm.date - *arightcomm.date;
- return diff.is_negative();
- }
+ std::pair<commodities_t::iterator, bool> result
+ = commodities.insert(commodity.get());
+ if (! result.second) {
+ assert(false);
+ return NULL;
+ } else {
+ return commodity.release();
+ }
+}
- if (! aleftcomm.tag && arightcomm.tag)
- return true;
- if (aleftcomm.tag && ! arightcomm.tag)
- return false;
+commodity_t * commodity_pool_t::find_or_create(commodity_t& comm,
+ const annotation_t& details)
+{
+ assert(comm);
+ assert(details);
- if (aleftcomm.tag && arightcomm.tag)
- return *aleftcomm.tag < *arightcomm.tag;
+ string name = make_qualified_name(comm, details);
+ assert(! name.empty());
- assert(false);
- return true;
+ if (commodity_t * ann_comm = find(name)) {
+ assert(ann_comm->annotated && ann_comm->as_annotated().details);
+ return ann_comm;
}
+ return create(comm, details, name);
}
} // namespace ledger
diff --git a/src/commodity.h b/src/commodity.h
index def41778..29534729 100644
--- a/src/commodity.h
+++ b/src/commodity.h
@@ -53,232 +53,229 @@ namespace ledger {
#define COMMODITY_STYLE_NOMARKET 0x0010
#define COMMODITY_STYLE_BUILTIN 0x0020
-typedef std::map<const moment_t, amount_t> history_map;
-typedef std::pair<const moment_t, amount_t> history_pair;
-
-class commodity_base_t;
-
-typedef std::map<const string, commodity_base_t *> base_commodities_map;
-typedef std::pair<const string, commodity_base_t *> base_commodities_pair;
-
class commodity_base_t : public noncopyable
{
-public:
+private:
+ friend class commodity_pool_t;
friend class commodity_t;
friend class annotated_commodity_t;
- friend void amount_t::initialize();
- friend void amount_t::shutdown();
+ typedef std::map<const moment_t, amount_t> history_map;
+ typedef std::pair<const moment_t, amount_t> history_pair;
- friend void checked_delete<commodity_base_t>(commodity_base_t *);
+ struct history_t {
+ history_map prices;
+ ptime last_lookup;
+ };
- typedef uint_least32_t ident_t;
+ typedef uint_least8_t flags_t;
- ident_t ident;
- string name;
- string note;
+ flags_t flags;
+ string symbol;
amount_t::precision_t precision;
- unsigned char flags;
+ optional<string> name;
+ optional<string> note;
+ optional<history_t> history;
optional<amount_t> smaller;
optional<amount_t> larger;
- commodity_base_t() : precision(0), flags(COMMODITY_STYLE_DEFAULTS) {
+public:
+ explicit commodity_base_t()
+ : flags(COMMODITY_STYLE_DEFAULTS), precision(0) {
TRACE_CTOR(commodity_base_t, "");
}
-
- commodity_base_t(const commodity_base_t&) {
- TRACE_CTOR(commodity_base_t, "copy");
- assert(0);
- }
-
- commodity_base_t(const string& _symbol,
- unsigned int _precision = 0,
- unsigned int _flags = COMMODITY_STYLE_DEFAULTS)
- : precision(_precision), flags(_flags), symbol(_symbol) {
- TRACE_CTOR(commodity_base_t, "const string&, unsigned int, unsigned int");
+ explicit commodity_base_t
+ (const string& _symbol,
+ amount_t::precision_t _precision = 0,
+ unsigned int _flags = COMMODITY_STYLE_DEFAULTS)
+ : flags(_flags), symbol(_symbol), precision(_precision) {
+ TRACE_CTOR(commodity_base_t,
+ "const string&, amount_t::precision_t, unsigned int");
}
-
~commodity_base_t() {
TRACE_DTOR(commodity_base_t);
}
-
- static base_commodities_map commodities;
-
- static commodity_base_t * create(const string& symbol);
-
- string symbol;
-
- struct history_t {
- history_map prices;
- ptime last_lookup;
- history_t() : last_lookup() {}
- };
- optional<history_t> history;
-
- void add_price(const moment_t& date, const amount_t& price);
- bool remove_price(const moment_t& date);
- amount_t value(const moment_t& moment = now);
-
-public:
- class updater_t {
- public:
- virtual ~updater_t() {}
- virtual void operator()(commodity_base_t& commodity,
- const moment_t& moment,
- const moment_t& date,
- const moment_t& last,
- amount_t& price) = 0;
- };
- friend class updater_t;
-
- static updater_t * updater;
};
-typedef std::map<const string, commodity_t *> commodities_map;
-typedef std::pair<const string, commodity_t *> commodities_pair;
-
-typedef std::vector<commodity_t *> commodities_array;
+class annotated_commodity_t;
class commodity_t
- : public equality_comparable<commodity_t, noncopyable>
+ : public equality_comparable1<commodity_t, noncopyable>
{
- friend class annotated_commodity_t;
-
public:
- // This map remembers all commodities that have been defined.
-
- static commodities_map commodities;
- static commodities_array * commodities_by_ident;
- static bool commodities_sorted;
- static commodity_t * null_commodity;
- static commodity_t * default_commodity;
-
- static commodity_t * create(const string& symbol);
- static commodity_t * find(const string& name);
- static commodity_t * find_or_create(const string& symbol);
+ static bool symbol_needs_quotes(const string& symbol);
- static bool needs_quotes(const string& symbol);
+ typedef commodity_base_t::flags_t flags_t;
+ typedef commodity_base_t::history_t history_t;
+ typedef commodity_base_t::history_map history_map;
+ typedef commodity_base_t::history_pair history_pair;
+ typedef uint_least32_t ident_t;
- static void make_alias(const string& symbol,
- commodity_t * commodity);
-
- // These are specific to each commodity reference
-
- typedef unsigned long ident_t;
+ shared_ptr<commodity_base_t> base;
+ commodity_pool_t * parent_;
ident_t ident;
- commodity_base_t * base;
- string qualified_symbol;
+ optional<string> qualified_symbol;
+ optional<string> mapping_key_;
bool annotated;
public:
- explicit commodity_t() : base(NULL), annotated(false) {
+ explicit commodity_t(commodity_pool_t * _parent,
+ const shared_ptr<commodity_base_t>& _base)
+ : base(_base), parent_(_parent), annotated(false) {
TRACE_CTOR(commodity_t, "");
}
- commodity_t(const commodity_t& o)
- : ident(o.ident), base(o.base),
- qualified_symbol(o.qualified_symbol), annotated(o.annotated) {
- TRACE_CTOR(commodity_t, "copy");
- }
virtual ~commodity_t() {
TRACE_DTOR(commodity_t);
}
- operator bool() const {
- return this != null_commodity;
- }
+ operator bool() const;
+
virtual bool operator==(const commodity_t& comm) const {
if (comm.annotated)
return comm == *this;
- return base == comm.base;
+ return base.get() == comm.base.get();
}
+ commodity_pool_t& parent() const {
+ return *parent_;
+ }
+
+ annotated_commodity_t& as_annotated();
+ const annotated_commodity_t& as_annotated() const;
+
string base_symbol() const {
return base->symbol;
}
string symbol() const {
- return qualified_symbol;
+ return qualified_symbol ? *qualified_symbol : base_symbol();
}
- void write(std::ostream& out) const {
- out << symbol();
+ string mapping_key() const {
+ if (mapping_key_)
+ return *mapping_key_;
+ else
+ return base_symbol();
}
- string name() const {
+ optional<string> name() const {
return base->name;
}
- void set_name(const string& arg) {
+ void set_name(const optional<string>& arg = optional<string>()) {
base->name = arg;
}
- string note() const {
+ optional<string> note() const {
return base->note;
}
- void set_note(const string& arg) {
+ void set_note(const optional<string>& arg = optional<string>()) {
base->note = arg;
}
- unsigned char precision() const {
+ amount_t::precision_t precision() const {
return base->precision;
}
- void set_precision(unsigned char arg) {
+ void set_precision(amount_t::precision_t arg) {
base->precision = arg;
}
- unsigned char flags() const {
+ flags_t flags() const {
return base->flags;
}
- void set_flags(unsigned char arg) {
+ void set_flags(flags_t arg) {
base->flags = arg;
}
- void add_flags(unsigned char arg) {
+ void add_flags(flags_t arg) {
base->flags |= arg;
}
- void drop_flags(unsigned char arg) {
+ void drop_flags(flags_t arg) {
base->flags &= ~arg;
}
optional<amount_t> smaller() const {
return base->smaller;
}
- void set_smaller(const amount_t& arg) {
+ void set_smaller(const optional<amount_t>& arg = optional<amount_t>()) {
base->smaller = arg;
}
optional<amount_t> larger() const {
return base->larger;
}
- void set_larger(const amount_t& arg) {
+ void set_larger(const optional<amount_t>& arg = optional<amount_t>()) {
base->larger = arg;
}
- optional<commodity_base_t::history_t> history() const {
+ optional<history_t> history() const {
return base->history;
}
- void add_price(const moment_t& date, const amount_t& price) {
- return base->add_price(date, price);
- }
- bool remove_price(const moment_t& date) {
- return base->remove_price(date);
- }
- amount_t value(const moment_t& moment = now) const {
- return base->value(moment);
+ void add_price(const moment_t& date, const amount_t& price);
+ bool remove_price(const moment_t& date);
+
+ optional<amount_t> value(const optional<moment_t>& moment =
+ optional<moment_t>());
+
+ void write(std::ostream& out) const {
+ out << symbol();
}
bool valid() const;
};
-class annotated_commodity_t : public commodity_t
+inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) {
+ comm.write(out);
+ return out;
+}
+
+struct annotation_t : public equality_comparable<annotation_t>
{
- public:
- const commodity_t * ptr;
+ optional<amount_t> price;
+ optional<moment_t> date;
+ optional<string> tag;
- optional<amount_t> price;
- optional<moment_t> date;
- optional<string> tag;
+ explicit annotation_t(const optional<amount_t>& _price = optional<amount_t>(),
+ const optional<moment_t>& _date = optional<moment_t>(),
+ const optional<string>& _tag = optional<string>())
+ : price(_price), date(_date), tag(_tag) {}
- explicit annotated_commodity_t() {
+ operator bool() const {
+ return price || date || tag;
+ }
+
+ bool operator==(const annotation_t& rhs) const {
+ return (price == rhs.price &&
+ date == rhs.date &&
+ tag == rhs.tag);
+ }
+
+ void write(std::ostream& out) const {
+ out << "price " << (price ? price->to_string() : "NONE") << " "
+ << "date " << (date ? *date : moment_t()) << " "
+ << "tag " << (tag ? *tag : "NONE");
+ }
+
+ bool valid() const {
+ assert(*this);
+ }
+};
+
+inline std::ostream& operator<<(std::ostream& out, const annotation_t& details) {
+ details.write(out);
+ return out;
+}
+
+class annotated_commodity_t
+ : public commodity_t,
+ equality_comparable1<annotated_commodity_t, noncopyable>
+{
+public:
+ commodity_t * ptr;
+ annotation_t details;
+
+ explicit annotated_commodity_t(commodity_t * _ptr,
+ const annotation_t& _details)
+ : commodity_t(_ptr->parent_, _ptr->base), ptr(_ptr), details(_details) {
TRACE_CTOR(annotated_commodity_t, "");
annotated = true;
}
@@ -288,37 +285,129 @@ class annotated_commodity_t : public commodity_t
virtual bool operator==(const commodity_t& comm) const;
+ commodity_t& referent() {
+ return *ptr;
+ }
+ const commodity_t& referent() const {
+ return *ptr;
+ }
+
void write_annotations(std::ostream& out) const {
- annotated_commodity_t::write_annotations(out, price, date, tag);
+ annotated_commodity_t::write_annotations(out, details);
+ }
+
+ static void write_annotations(std::ostream& out,
+ const annotation_t& info);
+};
+
+struct compare_amount_commodities {
+ bool operator()(const amount_t * left, const amount_t * right) const;
+};
+
+class commodity_pool_t : public noncopyable
+{
+public:
+ /**
+ * The commodities collection in commodity_pool_t maintains pointers
+ * to all the commodities which have ever been created by the user,
+ * whether explicitly by calling the create methods of
+ * commodity_pool_t, or implicitly by parsing a commoditized amount.
+ *
+ * The `commodities' member variable represents a collection which
+ * is indexed by two vertices: first, and ordered sequence of unique
+ * integer which identify commodities by a numerical identifier; and
+ * second, by a hashed set of symbolic names which reflect how the
+ * commodity was referred to by the user.
+ */
+ typedef multi_index_container<
+ commodity_t *,
+ multi_index::indexed_by<
+ multi_index::ordered_unique<
+ multi_index::member<commodity_t,
+ commodity_t::ident_t, &commodity_t::ident> >,
+ multi_index::hashed_unique<
+ multi_index::const_mem_fun<commodity_t,
+ string, &commodity_t::mapping_key> >
+ >
+ > commodities_t;
+
+ commodities_t commodities;
+ commodity_t * null_commodity;
+ commodity_t * default_commodity;
+
+private:
+ template<typename T>
+ struct first_initialized
+ {
+ typedef T result_type;
+
+ template<typename InputIterator>
+ T operator()(InputIterator first, InputIterator last) const
+ {
+ for (; first != last; first++)
+ if (*first)
+ return *first;
+ return T();
+ }
+ };
+
+public:
+ boost::signal<optional<amount_t>
+ (commodity_t& commodity,
+ const optional<moment_t>& date,
+ const optional<moment_t>& moment,
+ const optional<moment_t>& last),
+ first_initialized<optional<amount_t> > > get_quote;
+
+ explicit commodity_pool_t();
+
+ ~commodity_pool_t() {
+ typedef commodity_pool_t::commodities_t::nth_index<0>::type
+ commodities_by_ident;
+
+ commodities_by_ident& ident_index = commodities.get<0>();
+ for (commodities_by_ident::iterator i = ident_index.begin();
+ i != ident_index.end();
+ i++)
+ checked_delete(*i);
}
- static void write_annotations(std::ostream& out,
- const optional<amount_t>& price,
- const optional<moment_t>& date,
- const optional<string>& tag);
+ commodity_t * create(const string& symbol);
+ commodity_t * find(const string& name);
+ commodity_t * find(const commodity_t::ident_t ident);
+ commodity_t * find_or_create(const string& symbol);
- private:
- static commodity_t * create(const commodity_t& comm,
- const optional<amount_t>& price,
- const optional<moment_t>& date,
- const optional<string>& tag,
- const string& mapping_key);
+ commodity_t * create(const string& symbol, const annotation_t& details);
+ commodity_t * find(const string& symbol, const annotation_t& details);
+ commodity_t * find_or_create(const string& symbol,
+ const annotation_t& details);
- static commodity_t * find_or_create(const commodity_t& comm,
- const optional<amount_t>& price,
- const optional<moment_t>& date,
- const optional<string>& tag);
+ commodity_t * create(commodity_t& comm,
+ const annotation_t& details,
+ const string& mapping_key);
- friend class amount_t;
-};
+ commodity_t * find_or_create(commodity_t& comm,
+ const annotation_t& details);
-inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) {
- out << comm.symbol();
- return out;
-}
+ void parse_amount(amount_t& amount, std::istream& in,
+ amount_t::flags_t flags = 0) {
+ amount.parse(*this, in, flags);
+ }
+ void parse_amount(amount_t& amount, const string& str,
+ amount_t::flags_t flags = 0) {
+ amount.parse(*this, str, flags);
+ }
-struct compare_amount_commodities {
- bool operator()(const amount_t * left, const amount_t * right) const;
+ amount_t parse_amount(std::istream& in, amount_t::flags_t flags = 0) {
+ amount_t temp;
+ parse_amount(temp, in, flags);
+ return temp;
+ }
+ amount_t parse_amount(const string& str, amount_t::flags_t flags = 0) {
+ amount_t temp;
+ parse_amount(temp, str, flags);
+ return temp;
+ }
};
} // namespace ledger
diff --git a/src/gnucash.cc b/src/gnucash.cc
index 0fb62bf0..50b1f8f8 100644
--- a/src/gnucash.cc
+++ b/src/gnucash.cc
@@ -191,7 +191,7 @@ void dataHandler(void *userData, const char *s, int len)
string symbol(s, len);
if (symbol == "USD") symbol = "$";
- parser->curr_comm = commodity_t::find_or_create(symbol);
+ parser->curr_comm = amount_t::default_pool->find_or_create(symbol);
assert(parser->curr_comm);
if (symbol != "$")
@@ -320,7 +320,7 @@ unsigned int gnucash_parser_t::parse(std::istream& in,
// GnuCash uses the USD commodity without defining it, which really
// means $.
- commodity_t * usd = commodity_t::find_or_create("$");
+ commodity_t * usd = amount_t::default_pool->find_or_create("$");
usd->set_precision(2);
usd->add_flags(COMMODITY_STYLE_THOUSANDS);
diff --git a/src/journal.cc b/src/journal.cc
index 109d07f4..7674bd4d 100644
--- a/src/journal.cc
+++ b/src/journal.cc
@@ -115,8 +115,9 @@ bool entry_base_t::finalize()
annotated_commodity_t&
ann_comm(static_cast<annotated_commodity_t&>
((*x)->amount->commodity()));
- if (ann_comm.price)
- balance += *ann_comm.price * (*x)->amount->number() - *((*x)->cost);
+ if (ann_comm.details.price)
+ balance += (*ann_comm.details.price * (*x)->amount->number() -
+ *((*x)->cost));
}
} else {
saw_null = true;
@@ -173,9 +174,9 @@ bool entry_base_t::finalize()
if ((*x)->amount->commodity() &&
! (*x)->amount->commodity().annotated)
(*x)->amount->annotate_commodity
- (per_unit_cost.abs(),
- entry ? entry->actual_date() : optional<moment_t>(),
- entry ? entry->code : optional<string>());
+ (annotation_t(per_unit_cost.abs(),
+ entry ? entry->actual_date() : optional<moment_t>(),
+ entry ? entry->code : optional<string>()));
(*x)->cost = - (per_unit_cost * (*x)->amount->number());
balance += *(*x)->cost;
@@ -610,14 +611,6 @@ bool journal_t::valid() const
return false;
}
- for (commodities_map::const_iterator i = commodity_t::commodities.begin();
- i != commodity_t::commodities.end();
- i++)
- if (! (*i).second->valid()) {
- DEBUG("ledger.validate", "journal_t: commodity not valid");
- return false;
- }
-
return true;
}
diff --git a/src/main.cc b/src/main.cc
index ae3d0d46..25a5fc18 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -408,26 +408,26 @@ int main(int argc, char * argv[], char * envp[])
#if defined(TRACING_ON)
if (i + 1 < argc && std::strcmp(argv[i], "--trace") == 0) {
ledger::_log_level = LOG_TRACE;
- ledger::_trace_level = lexical_cast<int>(argv[i + 1]);
+ ledger::_trace_level = std::atoi(argv[i + 1]);
i++;
}
#endif
}
+ IF_VERIFY()
+ initialize_memory_tracing();
+
try {
std::ios::sync_with_stdio(false);
boost::filesystem::path::default_name_check
(boost::filesystem::portable_posix_name);
- ledger::initialize();
-
-#if ! defined(FULL_DEBUG)
- ledger::do_cleanup = false;
-#endif
INFO("Ledger starting");
std::auto_ptr<ledger::session_t> session(new ledger::session_t);
+ ledger::set_session_context(session.get());
+
#if 0
session->register_parser(new binary_parser_t);
#endif
@@ -445,9 +445,11 @@ int main(int argc, char * argv[], char * envp[])
status = read_and_report(report.get(), argc, argv, envp);
- IF_VERIFY() {
+ if (! DO_VERIFY()) {
report.release();
session.release();
+ } else {
+ ledger::set_session_context();
}
}
#if 0
@@ -480,8 +482,12 @@ int main(int argc, char * argv[], char * envp[])
status = _status;
}
- IF_VERIFY()
- ledger::shutdown();
+ IF_VERIFY() {
+ INFO("Ledger ended (Boost/libstdc++ may still hold memory)");
+ shutdown_memory_tracing();
+ } else {
+ INFO("Ledger ended");
+ }
return status;
}
diff --git a/src/qif.cc b/src/qif.cc
index 2f1d720c..677b907e 100644
--- a/src/qif.cc
+++ b/src/qif.cc
@@ -110,7 +110,7 @@ unsigned int qif_parser_t::parse(std::istream& in,
unsigned char prec = xact->amount->commodity().precision();
if (! def_commodity) {
- def_commodity = commodity_t::find_or_create("$");
+ def_commodity = amount_t::default_pool->find_or_create("$");
assert(def_commodity);
}
xact->amount->set_commodity(*def_commodity);
diff --git a/src/quotes.cc b/src/quotes.cc
index 21ed5998..d40106f3 100644
--- a/src/quotes.cc
+++ b/src/quotes.cc
@@ -2,31 +2,36 @@
namespace ledger {
-void quotes_by_script::operator()(commodity_base_t& commodity,
- const ptime& moment,
- const ptime& date,
- const ptime& last,
- amount_t& price)
+optional<amount_t>
+quotes_by_script::operator()(commodity_t& commodity,
+ const optional<moment_t>& date,
+ const optional<moment_t>& moment,
+ const optional<moment_t>& last)
{
LOGGER("quotes.download");
- DEBUG_("commodity: " << commodity.symbol);
- DEBUG_(" now: " << now);
- DEBUG_(" moment: " << moment);
- DEBUG_(" date: " << date);
- DEBUG_(" last: " << last);
-
- if (SHOW_DEBUG_() && commodity.history)
- DEBUG_("last_lookup: " << commodity.history->last_lookup);
+ IF_DEBUG_() {
+ DEBUG_("commodity: " << commodity.symbol());
+ DEBUG_(" now: " << now);
+ if (date)
+ DEBUG_(" date: " << date);
+ if (moment)
+ DEBUG_(" moment: " << moment);
+ if (last)
+ DEBUG_(" last: " << last);
+ if (commodity.history())
+ DEBUG_("last_lookup: " << commodity.history()->last_lookup);
+ }
DEBUG_("pricing_leeway is " << pricing_leeway);
- if ((commodity.history &&
- (time_now - commodity.history->last_lookup) < pricing_leeway) ||
- (time_now - last) < pricing_leeway ||
- (price && moment > date && (moment - date) <= pricing_leeway))
- return;
+ if ((commodity.history() &&
+ (now - commodity.history()->last_lookup) < pricing_leeway) ||
+ (last && (now - *last) < pricing_leeway) ||
+ (moment && date && *moment > *date &&
+ (*moment - *date) <= pricing_leeway))
+ return optional<amount_t>();
- DEBUG_("downloading quote for symbol " << commodity.symbol);
+ DEBUG_("downloading quote for symbol " << commodity.symbol());
char buf[256];
buf[0] = '\0';
@@ -34,7 +39,7 @@ void quotes_by_script::operator()(commodity_base_t& commodity,
bool success = true;
if (FILE * fp = popen((string("getquote \"") +
- commodity.symbol + "\"").c_str(), "r")) {
+ commodity.base_symbol() + "\"").c_str(), "r")) {
if (feof(fp) || ! fgets(buf, 255, fp))
success = false;
if (pclose(fp) != 0)
@@ -49,31 +54,31 @@ void quotes_by_script::operator()(commodity_base_t& commodity,
DEBUG_("downloaded quote: " << buf);
+ amount_t price;
price.parse(buf);
commodity.add_price(now, price);
- commodity.history->last_lookup = time_now;
+ commodity.history()->last_lookup = now;
cache_dirty = true;
- if (price) {
- assert(! price_db.empty());
+ assert(! price_db.empty());
#if defined(__GNUG__) && __GNUG__ < 3
- ofstream database(price_db, ios::out | ios::app);
+ ofstream database(price_db, ios::out | ios::app);
#else
- ofstream database(price_db, std::ios_base::out | std::ios_base::app);
+ ofstream database(price_db, std::ios_base::out | std::ios_base::app);
#endif
#if 0
- // jww (2007-04-18): Need to convert to local time and print
- // here, print with UTC timezone specifier
- database << "P " << now.to_string("%Y/%m/%d %H:%M:%S")
- << " " << commodity.symbol << " " << price << endl;
+ // jww (2007-04-18): Need to convert to local time and print
+ // here, print with UTC timezone specifier
+ database << "P " << now.to_string("%Y/%m/%d %H:%M:%S")
+ << " " << commodity.symbol << " " << price << endl;
#endif
- }
+ return price;
} else {
throw_(download_error,
- "Failed to download price for '" << commodity.symbol <<
- "' (command: \"getquote " << commodity.symbol << "\")");
+ "Failed to download price for '" << commodity.symbol() <<
+ "' (command: \"getquote " << commodity.base_symbol() << "\")");
}
}
diff --git a/src/quotes.h b/src/quotes.h
index 8ac3b9d4..5f39f041 100644
--- a/src/quotes.h
+++ b/src/quotes.h
@@ -5,7 +5,7 @@
namespace ledger {
-class quotes_by_script : public commodity_base_t::updater_t
+class quotes_by_script
{
path price_db;
time_duration pricing_leeway;
@@ -18,11 +18,11 @@ class quotes_by_script : public commodity_base_t::updater_t
: price_db(_price_db), pricing_leeway(_pricing_leeway),
cache_dirty(_cache_dirty) {}
- virtual void operator()(commodity_base_t& commodity,
- const ptime& moment,
- const ptime& date,
- const ptime& last,
- amount_t& price);
+ virtual optional<amount_t>
+ operator()(commodity_t& commodity,
+ const optional<moment_t>& date,
+ const optional<moment_t>& moment,
+ const optional<moment_t>& last);
};
DECLARE_EXCEPTION(download_error);
diff --git a/src/session.cc b/src/session.cc
index 5f452f25..d53faf09 100644
--- a/src/session.cc
+++ b/src/session.cc
@@ -2,6 +2,41 @@
namespace ledger {
+session_t * session_t::current = NULL;
+
+#if 0
+boost::mutex session_t::session_mutex;
+#endif
+
+static void initialize();
+static void shutdown();
+
+void set_session_context(session_t * session)
+{
+#if 0
+ session_t::session_mutex.lock();
+#endif
+
+ if (session && ! session_t::current) {
+ initialize();
+ }
+ else if (! session && session_t::current) {
+ shutdown();
+#if 0
+ session_t::session_mutex.unlock();
+#endif
+ }
+
+ session_t::current = session;
+}
+
+void release_session_context()
+{
+#if 0
+ session_t::session_mutex.unlock();
+#endif
+}
+
unsigned int session_t::read_journal(std::istream& in,
journal_t * journal,
account_t * master,
@@ -203,26 +238,16 @@ xml::xpath_t::op_t * session_t::lookup(const string& name)
// jww (2007-04-26): All of Ledger should be accessed through a
// session_t object
-void initialize()
+static void initialize()
{
- IF_VERIFY()
- initialize_memory_tracing();
-
amount_t::initialize();
xml::xpath_t::initialize();
}
-void shutdown()
+static void shutdown()
{
xml::xpath_t::shutdown();
amount_t::shutdown();
-
- IF_VERIFY() {
- INFO("Ledger ended (Boost/libstdc++ may still hold memory)");
- shutdown_memory_tracing();
- } else {
- INFO("Ledger ended");
- }
}
} // namespace ledger
diff --git a/src/session.h b/src/session.h
index 6fb2c21d..c8d83065 100644
--- a/src/session.h
+++ b/src/session.h
@@ -10,6 +10,8 @@ namespace ledger {
class session_t : public xml::xpath_t::scope_t
{
public:
+ static session_t * current;
+
path data_file;
optional<path> init_file;
optional<path> cache_file;
@@ -185,8 +187,16 @@ class session_t : public xml::xpath_t::scope_t
#endif
};
-void initialize();
-void shutdown();
+/**
+ * This sets the current session context, transferring all static
+ * globals to point at the data structures related to this session.
+ * Although Ledger itself is not thread-safe, by locking, switching
+ * session context, then unlocking after the operation is done,
+ * multiple threads can sequentially make use of the library. Thus, a
+ * session_t maintains all of the information relating to a single
+ * usage of the Ledger library.
+ */
+void set_session_context(session_t * session = NULL);
} // namespace ledger
diff --git a/src/system.hh b/src/system.hh
index 34319d75..2cc03413 100644
--- a/src/system.hh
+++ b/src/system.hh
@@ -98,7 +98,7 @@ extern "C" {
#endif
#include <boost/algorithm/string/predicate.hpp>
-#include <boost/any.hpp>
+#include <boost/cast.hpp>
#include <boost/current_function.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/filesystem/convenience.hpp>
@@ -106,11 +106,16 @@ extern "C" {
#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
-#include <boost/lexical_cast.hpp>
+#include <boost/function.hpp>
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/hashed_index.hpp>
+#include <boost/multi_index/key_extractors.hpp>
+#include <boost/multi_index/ordered_index.hpp>
#include <boost/operators.hpp>
#include <boost/optional.hpp>
#include <boost/ptr_container/ptr_list.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
#include <boost/regex.hpp>
+#include <boost/signal.hpp>
#endif // _SYSTEM_HH
diff --git a/src/textual.cc b/src/textual.cc
index 5fe13e6d..6b36a4ac 100644
--- a/src/textual.cc
+++ b/src/textual.cc
@@ -263,9 +263,9 @@ transaction_t * parse_transaction(char * line,
if (xact->amount->commodity() &&
! xact->amount->commodity().annotated)
- xact->amount->annotate_commodity(per_unit_cost,
- xact->entry->actual_date(),
- xact->entry->code);
+ xact->amount->annotate_commodity(annotation_t(per_unit_cost,
+ xact->entry->actual_date(),
+ xact->entry->code));
DEBUG("ledger.textual.parse", "line " << linenum << ": " <<
"Total cost is " << *xact->cost);
@@ -718,7 +718,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
case 'D': { // a default commodity for "entry"
amount_t amt(skip_ws(line + 1));
- commodity_t::default_commodity = &amt.commodity();
+ amount_t::default_pool->default_commodity = &amt.commodity();
break;
}
@@ -756,7 +756,8 @@ unsigned int textual_parser_t::parse(std::istream& in,
parse_symbol(symbol_and_price, symbol);
amount_t price(symbol_and_price);
- if (commodity_t * commodity = commodity_t::find_or_create(symbol))
+ if (commodity_t * commodity =
+ amount_t::default_pool->find_or_create(symbol))
commodity->add_price(datetime, price);
break;
}
@@ -766,7 +767,8 @@ unsigned int textual_parser_t::parse(std::istream& in,
string symbol;
parse_symbol(p, symbol);
- if (commodity_t * commodity = commodity_t::find_or_create(symbol))
+ if (commodity_t * commodity =
+ amount_t::default_pool->find_or_create(symbol))
commodity->add_flags(COMMODITY_STYLE_NOMARKET);
break;
}
@@ -774,7 +776,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
case 'Y': // set current year
#if 0
// jww (2007-04-18): Need to set this up again
- date_t::current_year = lexical_cast<int>(skip_ws(line + 1));
+ date_t::current_year = std::atoi(skip_ws(line + 1));
#endif
break;
diff --git a/src/utils.h b/src/utils.h
index 65071035..4a7213b7 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -1,6 +1,11 @@
#ifndef _UTILS_H
#define _UTILS_H
+#if defined(FULL_DEBUG)
+#define BOOST_MULTI_INDEX_ENABLE_SAFE_MODE 1
+#define BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING 1
+#endif
+
#include <system.hh>
/**********************************************************************
diff --git a/src/value.cc b/src/value.cc
index b181df8f..e1cfa736 100644
--- a/src/value.cc
+++ b/src/value.cc
@@ -1647,7 +1647,7 @@ void value_t::in_place_cast(type_t cast_type)
break;
}
if (alldigits) {
- long temp = lexical_cast<long>((*(string **) data)->c_str());
+ long temp = std::atol((*(string **) data)->c_str());
destroy();
*(long *) data = temp;
} else {
@@ -1840,7 +1840,7 @@ bool value_t::realzero() const
return 0;
}
-value_t value_t::value(const moment_t& moment) const
+value_t value_t::value(const optional<moment_t>& moment) const
{
switch (type) {
case BOOLEAN:
@@ -1849,16 +1849,30 @@ value_t value_t::value(const moment_t& moment) const
throw_(value_error, "Cannot find the value of a date/time");
case INTEGER:
return *this;
- case AMOUNT:
- return ((amount_t *) data)->value(moment);
- case BALANCE:
- return ((balance_t *) data)->value(moment);
- case BALANCE_PAIR:
- return ((balance_pair_t *) data)->quantity.value(moment);
+
+ case AMOUNT: {
+ if (optional<amount_t> val = ((amount_t *) data)->value(moment))
+ return *val;
+ return false;
+ }
+ case BALANCE: {
+ if (optional<balance_t> bal = ((balance_t *) data)->value(moment))
+ return *bal;
+ return false;
+ }
+ case BALANCE_PAIR: {
+ if (optional<balance_t> bal =
+ ((balance_pair_t *) data)->quantity.value(moment))
+ return *bal;
+ return false;
+ }
+
case STRING:
throw_(value_error, "Cannot find the value of a string");
+
case XML_NODE:
return (*(xml::node_t **) data)->to_value().value(moment);
+
case POINTER:
throw_(value_error, "Cannot find the value of a pointer");
case SEQUENCE:
@@ -1954,45 +1968,37 @@ value_t value_t::unround() const
return value_t();
}
-value_t value_t::price() const
+value_t value_t::annotated_price() const
{
switch (type) {
case BOOLEAN:
- throw_(value_error, "Cannot find the price of a boolean");
+ throw_(value_error, "Cannot find the annotated price of a boolean");
case INTEGER:
return *this;
case DATETIME:
- throw_(value_error, "Cannot find the price of a date/time");
+ throw_(value_error, "Cannot find the annotated price of a date/time");
case AMOUNT: {
- optional<amount_t> temp = ((amount_t *) data)->price();
- if (! temp)
- return false;
- return *temp;
- }
- case BALANCE: {
- optional<balance_t> temp = ((balance_t *) data)->price();
- if (! temp)
- return false;
- return *temp;
- }
- case BALANCE_PAIR: {
- optional<balance_t> temp = ((balance_pair_t *) data)->price();
+ optional<amount_t> temp = ((amount_t *) data)->annotation_details().price;
if (! temp)
return false;
return *temp;
}
+ case BALANCE:
+ throw_(value_error, "Cannot find the annotated price of a balance");
+ case BALANCE_PAIR:
+ throw_(value_error, "Cannot find the annotated price of a balance pair");
case STRING:
- throw_(value_error, "Cannot find the price of a string");
+ throw_(value_error, "Cannot find the annotated price of a string");
case XML_NODE:
- return (*(xml::node_t **) data)->to_value().price();
+ return (*(xml::node_t **) data)->to_value().annotated_price();
case POINTER:
- throw_(value_error, "Cannot find the price of a pointer");
+ throw_(value_error, "Cannot find the annotated price of a pointer");
case SEQUENCE:
- throw_(value_error, "Cannot find the price of a sequence");
+ throw_(value_error, "Cannot find the annotated price of a sequence");
default:
assert(0);
@@ -2002,46 +2008,38 @@ value_t value_t::price() const
return value_t();
}
-value_t value_t::date() const
+value_t value_t::annotated_date() const
{
switch (type) {
case BOOLEAN:
- throw_(value_error, "Cannot find the date of a boolean");
+ throw_(value_error, "Cannot find the annotated date of a boolean");
case INTEGER:
- throw_(value_error, "Cannot find the date of an integer");
+ throw_(value_error, "Cannot find the annotated date of an integer");
case DATETIME:
return *this;
case AMOUNT: {
- optional<moment_t> temp = ((amount_t *) data)->date();
- if (! temp)
- return false;
- return *temp;
- }
- case BALANCE: {
- optional<moment_t> temp = ((balance_t *) data)->date();
- if (! temp)
- return false;
- return *temp;
- }
- case BALANCE_PAIR: {
- optional<moment_t> temp = ((balance_pair_t *) data)->date();
+ optional<moment_t> temp = ((amount_t *) data)->annotation_details().date;
if (! temp)
return false;
return *temp;
}
+ case BALANCE:
+ throw_(value_error, "Cannot find the annotated date of a balance");
+ case BALANCE_PAIR:
+ throw_(value_error, "Cannot find the annotated date of a balance pair");
case STRING:
- throw_(value_error, "Cannot find the date of a string");
+ throw_(value_error, "Cannot find the annotated date of a string");
case XML_NODE:
- return (*(xml::node_t **) data)->to_value().date();
+ return (*(xml::node_t **) data)->to_value().annotated_date();
case POINTER:
- throw_(value_error, "Cannot find the date of a pointer");
+ throw_(value_error, "Cannot find the annotated date of a pointer");
case SEQUENCE:
- throw_(value_error, "Cannot find the date of a sequence");
+ throw_(value_error, "Cannot find the annotated date of a sequence");
default:
assert(0);
@@ -2051,46 +2049,38 @@ value_t value_t::date() const
return value_t();
}
-value_t value_t::tag() const
+value_t value_t::annotated_tag() const
{
switch (type) {
case BOOLEAN:
- throw_(value_error, "Cannot find the date of a boolean");
+ throw_(value_error, "Cannot find the annotated tag of a boolean");
case INTEGER:
- throw_(value_error, "Cannot find the date of an integer");
+ throw_(value_error, "Cannot find the annotated tag of an integer");
case DATETIME:
return *this;
case AMOUNT: {
- optional<string> temp = ((amount_t *) data)->tag();
- if (! temp)
- return false;
- return *temp;
- }
- case BALANCE: {
- optional<string> temp = ((balance_t *) data)->tag();
- if (! temp)
- return false;
- return *temp;
- }
- case BALANCE_PAIR: {
- optional<string> temp = ((balance_pair_t *) data)->tag();
+ optional<string> temp = ((amount_t *) data)->annotation_details().tag;
if (! temp)
return false;
return *temp;
}
+ case BALANCE:
+ throw_(value_error, "Cannot find the annotated tag of a balance");
+ case BALANCE_PAIR:
+ throw_(value_error, "Cannot find the annotated tag of a balance pair");
case STRING:
- throw_(value_error, "Cannot find the date of a string");
+ throw_(value_error, "Cannot find the annotated tag of a string");
case XML_NODE:
- return (*(xml::node_t **) data)->to_value().date();
+ return (*(xml::node_t **) data)->to_value().annotated_tag();
case POINTER:
- throw_(value_error, "Cannot find the date of a pointer");
+ throw_(value_error, "Cannot find the annotated tag of a pointer");
case SEQUENCE:
- throw_(value_error, "Cannot find the date of a sequence");
+ throw_(value_error, "Cannot find the annotated tag of a sequence");
default:
assert(0);
diff --git a/src/value.h b/src/value.h
index b44cdd33..d8c18fe4 100644
--- a/src/value.h
+++ b/src/value.h
@@ -363,9 +363,9 @@ class value_t
value_t abs() const;
void in_place_cast(type_t cast_type);
value_t cost() const;
- value_t price() const;
- value_t date() const;
- value_t tag() const;
+ value_t annotated_price() const;
+ value_t annotated_date() const;
+ value_t annotated_tag() const;
value_t cast(type_t cast_type) const {
value_t temp(*this);
@@ -379,7 +379,8 @@ class value_t
value_t& add(const amount_t& amount,
const optional<amount_t>& cost = optional<amount_t>());
- value_t value(const moment_t& moment) const;
+ value_t value(const optional<moment_t>& moment =
+ optional<moment_t>()) const;
void in_place_reduce();
value_t reduce() const {
diff --git a/src/xmlparse.cc b/src/xmlparse.cc
index 94b76ad7..c54e5969 100644
--- a/src/xmlparse.cc
+++ b/src/xmlparse.cc
@@ -104,7 +104,7 @@ static void endElement(void *userData, const char *name)
}
else if (std::strcmp(name, "symbol") == 0) {
assert(! curr_comm);
- curr_comm = commodity_t::find_or_create(data);
+ curr_comm = amount_t::default_pool->find_or_create(data);
assert(curr_comm);
curr_comm->add_flags(COMMODITY_STYLE_SUFFIXED);
if (! comm_flags.empty()) {
diff --git a/src/xpath.cc b/src/xpath.cc
index a7e1e661..fd574cee 100644
--- a/src/xpath.cc
+++ b/src/xpath.cc
@@ -654,7 +654,7 @@ xpath_t::parse_value_term(std::istream& in, unsigned short tflags) const
int id = -1;
if (std::isdigit(ident[0])) {
node.reset(new op_t(op_t::ARG_INDEX));
- node->arg_index = lexical_cast<unsigned int>(ident.c_str());
+ node->arg_index = std::atol(ident.c_str());
}
else if ((id = document_t::lookup_builtin_id(ident)) != -1) {
node.reset(new op_t(op_t::NODE_ID));
@@ -2286,7 +2286,7 @@ bool xpath_t::op_t::write(std::ostream& out,
}
if (! symbol.empty()) {
- if (commodity_t::find(symbol))
+ if (amount_t::default_pool->find(symbol))
out << '@';
out << symbol;
}