diff options
author | John Wiegley <johnw@newartisans.com> | 2007-05-21 20:39:05 +0000 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2008-04-13 03:39:05 -0400 |
commit | 8b606e7c850d6a5d8f680574beca4d5760486d32 (patch) | |
tree | 66dfde530a700f67c722f5c55a34368a47722afa | |
parent | c331dcf09e307747757082a6f868238954e19e55 (diff) | |
download | fork-ledger-8b606e7c850d6a5d8f680574beca4d5760486d32.tar.gz fork-ledger-8b606e7c850d6a5d8f680574beca4d5760486d32.tar.bz2 fork-ledger-8b606e7c850d6a5d8f680574beca4d5760486d32.zip |
Completed documentation for balance.h.
-rw-r--r-- | configure.in | 2 | ||||
-rw-r--r-- | src/amount.h | 14 | ||||
-rw-r--r-- | src/balance.cc | 216 | ||||
-rw-r--r-- | src/balance.h | 462 | ||||
-rw-r--r-- | src/balpair.h | 24 | ||||
-rw-r--r-- | src/journal.h | 3 | ||||
-rw-r--r-- | src/value.cc | 125 | ||||
-rw-r--r-- | src/xpath.cc | 2 |
8 files changed, 501 insertions, 347 deletions
diff --git a/configure.in b/configure.in index 20e5514c..a4459c80 100644 --- a/configure.in +++ b/configure.in @@ -3,7 +3,7 @@ AC_PREREQ(2.61) -AC_INIT([ledger],[3.0-git],[johnw@newartisans.com]) +AC_INIT([ledger],[3.0],[johnw@newartisans.com]) AC_CONFIG_SRCDIR(ledger) AM_INIT_AUTOMAKE([dist-bzip2]) diff --git a/src/amount.h b/src/amount.h index 0ca290a8..b054bb69 100644 --- a/src/amount.h +++ b/src/amount.h @@ -72,9 +72,9 @@ DECLARE_EXCEPTION(amount_error); */ class amount_t : public ordered_field_operators<amount_t, - ordered_field_operators<amount_t, long, + ordered_field_operators<amount_t, double, ordered_field_operators<amount_t, unsigned long, - ordered_field_operators<amount_t, double> > > > + ordered_field_operators<amount_t, long> > > > { // jww (2007-05-03): Make this private, and then make // ledger::initialize into a member function of session_t. @@ -165,9 +165,9 @@ public: * precision or sign is lost in any of these conversions. The * resulting commodity is always `commodity_t::null_commodity'. * - * amount_t(string), amount_t(char*) both convert from a string - * representation of an amount, which may or may not include a - * commodity. This is the proper way to initialize an amount like + * amount_t(string), amount_t(const char *) both convert from a + * string representation of an amount, which may or may not include + * a commodity. This is the proper way to initialize an amount like * '$100.00'. */ amount_t() : quantity(NULL), commodity_(NULL) { @@ -322,7 +322,7 @@ public: * * Further, for the sake of efficiency and avoiding temporary * objects, the following methods support "in-place" variants that - * act on the value itself and return a reference to the result + * act on the amount itself and return a reference to the result * (`*this'): * * in_place_negate() @@ -594,7 +594,7 @@ public: * and two arguments with default values: * * print(ostream, bool omit_commodity = false, bool full_precision = - * false) prits an amounts to the given output stream, using its + * false) prints an amounts to the given output stream, using its * commodity's default display characteristics. If `omit_commodity' * is true, the commodity will not be displayed, only the amount * (although the commodity's display precision is still used). If diff --git a/src/balance.cc b/src/balance.cc index 80ddd2e4..80637221 100644 --- a/src/balance.cc +++ b/src/balance.cc @@ -33,98 +33,169 @@ namespace ledger { +balance_t& balance_t::operator+=(const balance_t& bal) +{ + for (amounts_map::const_iterator i = bal.amounts.begin(); + i != bal.amounts.end(); + i++) + *this += i->second; + return *this; +} + +balance_t& balance_t::operator+=(const amount_t& amt) +{ + if (amt.is_null()) + throw_(balance_error, + "Cannot add an uninitialized amount to a balance"); + + if (amt.is_realzero()) + return *this; + + amounts_map::iterator i = amounts.find(&amt.commodity()); + if (i != amounts.end()) + i->second += amt; + else + amounts.insert(amounts_map::value_type(&amt.commodity(), amt)); + + return *this; +} + +balance_t& balance_t::operator-=(const balance_t& bal) +{ + for (amounts_map::const_iterator i = bal.amounts.begin(); + i != bal.amounts.end(); + i++) + *this -= i->second; + return *this; +} + +balance_t& balance_t::operator-=(const amount_t& amt) +{ + if (amt.is_null()) + throw_(balance_error, + "Cannot subtract an uninitialized amount from a balance"); + + if (amt.is_realzero()) + return *this; + + amounts_map::iterator i = amounts.find(&amt.commodity()); + if (i != amounts.end()) { + i->second -= amt; + if (i->second.is_realzero()) + amounts.erase(i); + } else { + amounts.insert(amounts_map::value_type(&amt.commodity(), amt.negate())); + } + return *this; +} + balance_t& balance_t::operator*=(const amount_t& amt) { + if (amt.is_null()) + throw_(balance_error, + "Cannot multiply a balance by an uninitialized amount"); + if (is_realzero()) { - return *this; + ; } else if (amt.is_realzero()) { - return *this = amt; + *this = amt; } else if (! amt.commodity()) { - // Multiplying by the null commodity causes all amounts to be - // increased by the same factor. + // Multiplying by an amount with no commodity causes all the + // component amounts to be increased by the same factor. for (amounts_map::iterator i = amounts.begin(); i != amounts.end(); i++) - (*i).second *= amt; + i->second *= amt; } else if (amounts.size() == 1) { - *this = (*amounts.begin()).second * amt; + // Multiplying by a commoditized amount is only valid if the sole + // commodity in the balance is of the same kind as the amount's + // commodity. + if (*amounts.begin()->first == amt.commodity()) + amounts.begin()->second *= amt; + else + throw_(balance_error, + "Cannot multiply a balance with annotated commodities by a commoditized amount"); } else { - amounts_map::iterator i = amounts.find(&amt.commodity()); - if (i != amounts.end()) { - (*i).second *= amt; - } else { - // Try stripping annotations before giving an error. - balance_t temp(strip_annotations()); - if (temp.amounts.size() == 1) { - return *this = (*temp.amounts.begin()).second * amt; - } else { - i = temp.amounts.find(&amt.commodity()); - if (i != temp.amounts.end()) - return *this = temp * amt; - } - - throw_(amount_error, "Attempt to multiply balance by a commodity" << - " not found in that balance: " << temp << " * " << amt); - } + assert(amounts.size() > 1); + throw_(balance_error, + "Cannot multiply a multi-commodity balance by a commoditized amount"); } return *this; } balance_t& balance_t::operator/=(const amount_t& amt) { - if (amt.is_realzero()) { - throw_(amount_error, "Divide by zero: " << *this << " / " << amt); + if (amt.is_null()) + throw_(balance_error, + "Cannot divide a balance by an uninitialized amount"); + + if (is_realzero()) { + ; } - else if (is_realzero()) { - return *this; + else if (amt.is_realzero()) { + throw_(balance_error, "Divide by zero"); } else if (! amt.commodity()) { - // Dividing by the null commodity causes all amounts to be - // decreased by the same factor. + // Dividing by an amount with no commodity causes all the + // component amounts to be divided by the same factor. for (amounts_map::iterator i = amounts.begin(); i != amounts.end(); i++) - (*i).second /= amt; + i->second /= amt; } - else if (amounts.size() == 1 && - (*amounts.begin()).first == &amt.commodity()) { - (*amounts.begin()).second /= amt; + else if (amounts.size() == 1) { + // Dividing by a commoditized amount is only valid if the sole + // commodity in the balance is of the same kind as the amount's + // commodity. + if (*amounts.begin()->first == amt.commodity()) + amounts.begin()->second /= amt; + else + throw_(balance_error, + "Cannot divide a balance with annotated commodities by a commoditized amount"); } else { - amounts_map::iterator i = amounts.find(&amt.commodity()); - if (i != amounts.end()) { - (*i).second /= amt; - } else { - // Try stripping annotations before giving an error. - balance_t temp(strip_annotations()); - if (temp.amounts.size() == 1 && - (*temp.amounts.begin()).first == &amt.commodity()) - return *this = temp / amt; - - throw_(amount_error, "Attempt to divide balance by a commodity" << - " not found in that balance: " << temp << " * " << amt); - } + assert(amounts.size() > 1); + throw_(balance_error, + "Cannot divide a multi-commodity balance by a commoditized amount"); } return *this; } +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++) + if (optional<amount_t> val = i->second.value(moment)) { + if (! temp) + temp = balance_t(); + *temp += *val; + } + + return temp; +} + optional<amount_t> -balance_t::amount(const optional<const commodity_t&>& commodity) const +balance_t::commodity_amount(const optional<const commodity_t&>& commodity) const { + // jww (2007-05-20): Needs work if (! commodity) { if (amounts.size() == 1) { amounts_map::const_iterator i = amounts.begin(); - return (*i).second; + return i->second; } else if (amounts.size() > 1) { // Try stripping annotations before giving an error. balance_t temp(strip_annotations()); if (temp.amounts.size() == 1) - return temp.amount(commodity); + return temp.commodity_amount(commodity); throw_(amount_error, "Requested amount of a balance with multiple commodities: " << temp); @@ -133,28 +204,11 @@ balance_t::amount(const optional<const commodity_t&>& commodity) const else if (amounts.size() > 0) { amounts_map::const_iterator i = amounts.find(&*commodity); if (i != amounts.end()) - return (*i).second; + return i->second; } return none; } -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++) - if (optional<amount_t> val = (*i).second.value(moment)) { - if (! temp) - temp = balance_t(); - *temp += *val; - } - - return temp; -} - balance_t balance_t::strip_annotations(const bool keep_price, const bool keep_date, const bool keep_tag) const @@ -164,7 +218,7 @@ balance_t balance_t::strip_annotations(const bool keep_price, for (amounts_map::const_iterator i = amounts.begin(); i != amounts.end(); i++) - temp += (*i).second.strip_annotations(keep_price, keep_date, keep_tag); + temp += i->second.strip_annotations(keep_price, keep_date, keep_tag); return temp; } @@ -185,8 +239,8 @@ void balance_t::print(std::ostream& out, for (amounts_map::const_iterator i = amounts.begin(); i != amounts.end(); i++) - if ((*i).second) - sorted.push_back(&(*i).second); + if (i->second) + sorted.push_back(&i->second); std::stable_sort(sorted.begin(), sorted.end(), compare_amount_commodities()); @@ -215,26 +269,4 @@ void balance_t::print(std::ostream& out, } } -#if 0 -balance_t::operator amount_t() const -{ - if (amounts.size() == 1) { - return (*amounts.begin()).second; - } - else if (amounts.size() == 0) { - return amount_t(); - } - else { - // Try stripping annotations before giving an error. - balance_t temp(strip_annotations()); - if (temp.amounts.size() == 1) - return (*temp.amounts.begin()).second; - - throw_(amount_error, - "Cannot convert a balance with " << - "multiple commodities to an amount: " << temp); - } -} -#endif - } // namespace ledger diff --git a/src/balance.h b/src/balance.h index e1fa9883..eec4716d 100644 --- a/src/balance.h +++ b/src/balance.h @@ -29,6 +29,18 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/** + * @file balance.h + * @author John Wiegley + * @date Sun May 20 15:28:44 2007 + * + * @brief Basic type for adding multiple commodities together. + * + * Unlike the amount_t class, which throws an exception if amounts of + * differing commodities are added or subtracted, the balance_t class + * is designed to allow this, tracking the amounts of each component + * commodity separately. + */ #ifndef _BALANCE_H #define _BALANCE_H @@ -36,14 +48,31 @@ namespace ledger { +DECLARE_EXCEPTION(balance_error); + +/** + * @class balance_t + * + * @brief A wrapper around amount_t allowing addition of multiple commodities. + * + * The balance_t class is appopriate for keeping a running balance + * where amounts of multiple commodities may be involved. + */ class balance_t : public equality_comparable<balance_t, equality_comparable<balance_t, amount_t, + equality_comparable<balance_t, double, + equality_comparable<balance_t, unsigned long, + equality_comparable<balance_t, long, additive<balance_t, additive<balance_t, amount_t, + additive<balance_t, double, + additive<balance_t, unsigned long, + additive<balance_t, long, multiplicative<balance_t, amount_t, + multiplicative<balance_t, double, multiplicative<balance_t, unsigned long, - multiplicative<balance_t, long> > > > > > > + multiplicative<balance_t, long> > > > > > > > > > > > > > { public: typedef std::map<const commodity_t *, amount_t> amounts_map; @@ -51,194 +80,411 @@ public: protected: amounts_map amounts; + // jww (2007-05-20): Remove these two by adding access methods friend class value_t; friend class entry_base_t; public: - // constructors + /** + * Constructors. balance_t supports similar forms of construction + * to amount_t. + * + * balance_t() creates an empty balance to which amounts or other + * balances may be added or subtracted. + * + * balance_t(amount_t) constructs a balance whose starting value is + * equal to the given amount. + * + * balance_t(double), balance_t(unsigned long) and balance_t(long) + * will construct an amount from their arguments and then construct + * a balance whose starting value is equal to that amount. This + * initial balance will have no commodity. + * + * balance_t(string) and balance_t(const char *) both convert from a + * string representation of an amount to a balance whose initial + * value is that amount. This is the proper way to initialize a + * balance like '$100.00'. + */ balance_t() { TRACE_CTOR(balance_t, ""); } - balance_t(const balance_t& bal) { - TRACE_CTOR(balance_t, "copy"); - for (amounts_map::const_iterator i = bal.amounts.begin(); - i != bal.amounts.end(); - i++) - *this += (*i).second; - } balance_t(const amount_t& amt) { TRACE_CTOR(balance_t, "const amount_t&"); - amounts.insert(amounts_map::value_type(&amt.commodity(), amt)); + if (amt.is_null()) + throw_(balance_error, + "Cannot initialize a balance from an uninitialized amount"); + if (! amt.is_realzero()) + amounts.insert(amounts_map::value_type(&amt.commodity(), amt)); + } + balance_t(const double val) { + TRACE_CTOR(balance_t, "const double"); + amounts.insert + (amounts_map::value_type(amount_t::current_pool->null_commodity, val)); + } + balance_t(const unsigned long val) { + TRACE_CTOR(balance_t, "const unsigned long"); + amounts.insert + (amounts_map::value_type(amount_t::current_pool->null_commodity, val)); + } + balance_t(const long val) { + TRACE_CTOR(balance_t, "const long"); + amounts.insert + (amounts_map::value_type(amount_t::current_pool->null_commodity, val)); } + + explicit balance_t(const string& val) { + TRACE_CTOR(balance_t, "const string&"); + amount_t temp(val); + amounts.insert(amounts_map::value_type(&temp.commodity(), temp)); + } + explicit balance_t(const char * val) { + TRACE_CTOR(balance_t, "const char *"); + amount_t temp(val); + amounts.insert(amounts_map::value_type(&temp.commodity(), temp)); + } + + /** + * Destructor. Destroys all of the accumulated amounts in the + * balance. + */ ~balance_t() { TRACE_DTOR(balance_t); } - // assignment operator + /** + * Assignment and copy operators. An balance may be assigned or copied. + */ + balance_t(const balance_t& bal) : amounts(bal.amounts) { + TRACE_CTOR(balance_t, "copy"); + } + balance_t& operator=(const balance_t& bal) { - if (this != &bal) { - amounts.clear(); - for (amounts_map::const_iterator i = bal.amounts.begin(); - i != bal.amounts.end(); - i++) - *this += (*i).second; - } + if (this != &bal) + amounts = bal.amounts; return *this; } + balance_t& operator=(const amount_t& amt) { + if (amt.is_null()) + throw_(balance_error, + "Cannot assign an uninitialized amount to a balance"); - int compare(const balance_t& bal) const; + amounts.clear(); + if (! amt.is_realzero()) + amounts.insert(amounts_map::value_type(&amt.commodity(), amt)); + + return *this; + } + balance_t& operator=(const string& str) { + return *this = balance_t(str); + } + balance_t& operator=(const char * str) { + return *this = balance_t(str); + } + + /** + * Comparison operators. Balances are fairly restrictive in terms + * of how they may be compared. They may be compared for equality + * or inequality, but this is all, since the concept of "less than" + * or "greater than" makes no sense when amounts of multiple + * commodities are involved. + * + * Balances may also be compared to amounts, in which case the sum + * of the balance must equal the amount exactly. + * + * If a comparison between balances is desired, the balances must + * first be rendered to value equivalent amounts using the `value' + * method, to determine a market valuation at some specific moment + * in time. + */ bool operator==(const balance_t& bal) const { amounts_map::const_iterator i, j; for (i = amounts.begin(), j = bal.amounts.begin(); i != amounts.end() && j != bal.amounts.end(); i++, j++) { - if (! ((*i).first == (*j).first && - (*i).second == (*j).second)) + if (! (i->first == j->first && i->second == j->second)) return false; } return i == amounts.end() && j == bal.amounts.end(); } bool operator==(const amount_t& amt) const { - return amounts.size() == 1 && amounts.begin()->second == amt; + if (amt.is_null()) + throw_(balance_error, + "Cannot compare a balance to an uninitialized amount"); + + if (amt.is_realzero()) + return amounts.empty(); + else + return amounts.size() == 1 && amounts.begin()->second == amt; } - // in-place arithmetic - balance_t& operator+=(const balance_t& bal) { - for (amounts_map::const_iterator i = bal.amounts.begin(); - i != bal.amounts.end(); - i++) - *this += (*i).second; - return *this; + template <typename T> + bool operator==(const T& val) const { + return *this == balance_t(val); } - balance_t& operator+=(const amount_t& amt) { - amounts_map::iterator i = amounts.find(&amt.commodity()); - if (i != amounts.end()) - (*i).second += amt; - else if (! amt.is_realzero()) - amounts.insert(amounts_map::value_type(&amt.commodity(), amt)); - return *this; + + /** + * Binary arithmetic operators. Balances support addition and + * subtraction of other balances or amounts, but multiplication and + * division are restricted to uncommoditized amounts only. + */ + balance_t& operator+=(const balance_t& bal); + balance_t& operator+=(const amount_t& amt); + balance_t& operator-=(const balance_t& bal); + balance_t& operator-=(const amount_t& amt); + + balance_t& operator*=(const amount_t& amt); + balance_t& operator/=(const amount_t& amt); + + /** + * Unary arithmetic operators. There are only a few unary methods + * support on balance: + * + * negate(), also unary minus (- x), returns a balance all of whose + * component amounts have been negated. In order words, it inverts + * the sign of all member amounts. + * + * abs() returns a balance where no component amount is negative. + * + * reduce() reduces the values in a balance to their most basic + * commodity forms, for amounts that utilize "scaling commodities". + * For example, a balance of 1h and 1m after reduction will be + * 3660s. + * + * unreduce(), if used with amounts that use "scaling commodities", + * yields the most compact form greater than 1.0 for each component + * amount. That is, a balance of 10m and 1799s will unreduce to + * 39.98m. + * + * value(optional<moment_t>) returns the total historical value for + * a balance -- the default moment returns a value based on the most + * recently known price -- based on the price history of its + * component commodities. See amount_t::value for an example. + * + * Further, for the sake of efficiency and avoiding temporary + * objects, the following methods support "in-place" variants act on + * the balance itself and return a reference to the result + * (`*this'): + * + * in_place_negate() + * in_place_reduce() + * in_place_unreduce() + */ + balance_t negate() const { + balance_t temp(*this); + temp.in_place_negate(); + return temp; } - balance_t& operator-=(const balance_t& bal) { - for (amounts_map::const_iterator i = bal.amounts.begin(); - i != bal.amounts.end(); + balance_t& in_place_negate() { + for (amounts_map::iterator i = amounts.begin(); + i != amounts.end(); i++) - *this -= (*i).second; + i->second.in_place_negate(); return *this; } - balance_t& operator-=(const amount_t& amt) { - amounts_map::iterator i = amounts.find(&amt.commodity()); - if (i != amounts.end()) { - (*i).second -= amt; - if ((*i).second.is_realzero()) - amounts.erase(i); - } - else if (! amt.is_realzero()) { - amounts.insert(amounts_map::value_type(&amt.commodity(), - amt)); - } - return *this; + balance_t operator-() const { + return negate(); } - balance_t& operator*=(const amount_t& amt); - balance_t& operator/=(const amount_t& amt); + balance_t abs() const { + balance_t temp; + for (amounts_map::const_iterator i = amounts.begin(); + i != amounts.end(); + i++) + temp += i->second.abs(); + return temp; + } - // unary negation - void in_place_negate() { - for (amounts_map::iterator i = amounts.begin(); + balance_t reduce() const { + balance_t temp(*this); + temp.in_place_reduce(); + return temp; + } + balance_t& in_place_reduce() { + balance_t temp; + // A temporary must be used here because reduction may cause + // multiple component amounts to collapse to the same commodity. + for (amounts_map::const_iterator i = amounts.begin(); i != amounts.end(); i++) - (*i).second = (*i).second.negate(); + temp += i->second.reduce(); + return *this = temp; } - balance_t negate() const { - balance_t temp = *this; - temp.in_place_negate(); + + balance_t unreduce() const { + balance_t temp(*this); + temp.in_place_unreduce(); return temp; } - balance_t operator-() const { - return negate(); + balance_t& in_place_unreduce() { + balance_t temp; + // A temporary must be used here because unreduction may cause + // multiple component amounts to collapse to the same commodity. + for (amounts_map::const_iterator i = amounts.begin(); + i != amounts.end(); + i++) + temp += i->second.unreduce(); + return *this = temp; } - // conversion operators + optional<balance_t> value(const optional<moment_t>& moment = none) const; + + /** + * Truth tests. An balance may be truth test in two ways: + * + * is_nonzero(), or operator bool, returns true if a balance's + * display value is not zero. + * + * is_zero() returns true if an balance's display value is zero. + * Thus, a balance containing $0.0001 is considered zero if the + * current display precision for dollars is two decimal places. + * + * is_realzero() returns true if an balance's actual value is zero. + * Thus, a balance containing $0.0001 is never considered realzero. + * + * is_empty() returns true if a balance has no amounts within it. + * This can occur after a balance has been default initialized, or + * if the exact amount it contains is subsequently subtracted from + * it. + */ operator bool() const { for (amounts_map::const_iterator i = amounts.begin(); i != amounts.end(); i++) - if ((*i).second) + if (i->second.is_nonzero()) return true; return false; } + bool is_zero() const { + if (is_empty()) + return true; + + for (amounts_map::const_iterator i = amounts.begin(); + i != amounts.end(); + i++) + if (! i->second.is_zero()) + return false; + return true; + } + bool is_realzero() const { - if (amounts.size() == 0) + if (is_empty()) return true; + for (amounts_map::const_iterator i = amounts.begin(); i != amounts.end(); i++) - if (! (*i).second.is_realzero()) + if (! i->second.is_realzero()) return false; return true; } + bool is_empty() const { + return amounts.size() == 0; + } + + /** + * Conversion methods. A balance can be converted to an amount, but + * only if contains a single component amount. + */ + amount_t to_amount() const { + if (is_empty()) + throw_(balance_error, "Cannot convert an empty balance to an amount"); + else if (amounts.size() == 1) + return amounts.begin()->second; + else + throw_(balance_error, + "Cannot convert a balance with multiple commodities to an amount"); + } + + /** + * Commodity-related methods. Balances support two + * commodity-related methods: + * + * commodity_count() returns the number of different commodities + * stored in the balance. + * + * commodity_amount(optional<commodity_t>) returns an (optional) + * amount for the given commodity within the balance; if no + * commodity is specified, it returns the (optional) uncommoditized + * component of the balance. If no matching element can be found, + * boost::none is returned. + */ + std::size_t commodity_count() const { + return amounts.size(); + } + optional<amount_t> - amount(const optional<const commodity_t&>& commodity = none) const; - optional<balance_t> value(const optional<moment_t>& moment = none) const; + commodity_amount(const optional<const commodity_t&>& commodity = none) const; + /** + * Annotated commodity methods. The amounts contained by a balance + * may use annotated commodities. The `strip_annotations' method + * will return a balance all of whose component amount have had + * their commodity annotations likewise stripped. See + * amount_t::strip_annotations for more details. + */ balance_t strip_annotations(const bool keep_price = amount_t::keep_price, const bool keep_date = amount_t::keep_date, const bool keep_tag = amount_t::keep_tag) const; + /** + * Printing methods. A balance may be output to a stream using the + * `print' method. There is also a global operator<< defined which + * simply calls print for a balance on the given stream. There is + * one form of the print method, which takes two required arguments + * and one arguments with a default value: + * + * print(ostream, int first_width, int latter_width) prints a + * balance to the given output stream, using each commodity's + * default display characteristics. The first_width parameter + * specifies the width that should be used for printing amounts + * (since they are likely to vary in width). The latter_width, if + * specified, gives the width to be used for each line after the + * first. This is useful when printing in a column which falls at + * the right-hand side of the screen. + * + * In addition to the width constraints, balances will also print + * with commodities in alphabetized order, regardless of the + * relative amounts of those commodities. There is no option to + * change this behavior. + */ void print(std::ostream& out, const int first_width, const int latter_width = -1) const; - balance_t abs() const { - balance_t temp = *this; - for (amounts_map::iterator i = temp.amounts.begin(); - i != temp.amounts.end(); - i++) - (*i).second = (*i).second.abs(); - return temp; - } - - void in_place_reduce() { - for (amounts_map::iterator i = amounts.begin(); - i != amounts.end(); - i++) - (*i).second.in_place_reduce(); - } - balance_t reduce() const { - balance_t temp(*this); - temp.in_place_reduce(); - return temp; - } - - void in_place_round() { - for (amounts_map::iterator i = amounts.begin(); - i != amounts.end(); - i++) - (*i).second = (*i).second.round(); - } - balance_t round() const { - balance_t temp(*this); - temp.in_place_round(); - return temp; - } - - balance_t unround() const { - balance_t temp; + /** + * Debugging methods. There are two methods defined to help with + * debugging: + * + * dump(ostream) dumps a balance to an output stream. There is + * little different from print(), it simply surrounds the display + * value with a marker, for example "BALANCE($1.00, DM 12.00)". + * This code is used by other dumping code elsewhere in Ledger. + * + * valid() returns true if the amounts within the balance are valid. + */ + void dump(std::ostream& out) const { + out << "BALANCE("; + bool first = true; for (amounts_map::const_iterator i = amounts.begin(); i != amounts.end(); - i++) - if ((*i).second.commodity()) - temp += (*i).second.unround(); - return temp; + i++) { + if (first) + first = false; + else + out << ", "; + i->second.print(out); + } + out << ")"; } bool valid() const { for (amounts_map::const_iterator i = amounts.begin(); i != amounts.end(); i++) - if (! (*i).second.valid()) + if (! i->second.valid()) return false; return true; } diff --git a/src/balpair.h b/src/balpair.h index c406394e..a4ba601f 100644 --- a/src/balpair.h +++ b/src/balpair.h @@ -47,7 +47,7 @@ class balance_pair_t multiplicative<balance_pair_t, long, multiplicative<balance_pair_t, amount_t> > > > > > > > > { - balance_t quantity; + balance_t quantity; optional<balance_t> cost; friend class value_t; @@ -158,8 +158,8 @@ public: } optional<amount_t> - amount(const optional<const commodity_t&>& commodity = none) const { - return quantity.amount(commodity); + commodity_amount(const optional<const commodity_t&>& commodity = none) const { + return quantity.commodity_amount(commodity); } optional<balance_t> value(const optional<moment_t>& moment = none) const { return quantity.value(moment); @@ -201,24 +201,6 @@ public: return temp; } - void in_place_round() { - quantity = quantity.round(); - if (cost) - cost = cost->round(); - } - balance_pair_t round() const { - balance_pair_t temp(*this); - temp.in_place_round(); - return temp; - } - - balance_pair_t unround() const { - balance_pair_t temp(quantity.unround()); - if (cost) - temp.cost = cost->unround(); - return temp; - } - friend std::ostream& operator<<(std::ostream& out, const balance_pair_t& bal_pair); }; diff --git a/src/journal.h b/src/journal.h index b32bdcaa..ed92bffb 100644 --- a/src/journal.h +++ b/src/journal.h @@ -219,9 +219,6 @@ class entry_context : public error_context { }; #endif -DECLARE_EXCEPTION(balance_error); - - class auto_entry_t : public entry_base_t { public: diff --git a/src/value.cc b/src/value.cc index 2985df45..a938b1bc 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1231,10 +1231,6 @@ value_t value_t::round() const return *this; case AMOUNT: return as_amount().round(); - case BALANCE: - return as_balance().round(); - case BALANCE_PAIR: - return as_balance_pair().round(); case XML_NODE: return as_xml_node()->to_value().round(); default: @@ -1248,48 +1244,23 @@ value_t value_t::round() const value_t value_t::unround() const { switch (type()) { - case BOOLEAN: - throw_(value_error, "Cannot un-round a boolean"); - case DATETIME: - throw_(value_error, "Cannot un-round a date/time"); - case INTEGER: return *this; - case AMOUNT: return as_amount().unround(); - case BALANCE: - return as_balance().unround(); - case BALANCE_PAIR: - return as_balance_pair().unround(); - - case STRING: - throw_(value_error, "Cannot un-round a string"); - case XML_NODE: return as_xml_node()->to_value().unround(); - - case POINTER: - throw_(value_error, "Cannot un-round a pointer"); - case SEQUENCE: - throw_(value_error, "Cannot un-round a sequence"); default: break; } - assert(false); + + throw_(value_error, "Cannot unround " << label()); return value_t(); } value_t value_t::annotated_price() const { switch (type()) { - case BOOLEAN: - throw_(value_error, "Cannot find the annotated price of a boolean"); - case INTEGER: - return *this; - case DATETIME: - throw_(value_error, "Cannot find the annotated price of a date/time"); - case AMOUNT: { optional<amount_t> temp = as_amount().annotation_details().price; if (! temp) @@ -1297,37 +1268,20 @@ value_t value_t::annotated_price() const 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 annotated price of a string"); - case XML_NODE: return as_xml_node()->to_value().annotated_price(); - case POINTER: - throw_(value_error, "Cannot find the annotated price of a pointer"); - case SEQUENCE: - throw_(value_error, "Cannot find the annotated price of a sequence"); - default: - assert(false); break; } - assert(false); + + throw_(value_error, "Cannot find the annotated price of " << label()); return value_t(); } value_t value_t::annotated_date() const { switch (type()) { - case BOOLEAN: - throw_(value_error, "Cannot find the annotated date of a boolean"); - case INTEGER: - throw_(value_error, "Cannot find the annotated date of an integer"); - case DATETIME: return *this; @@ -1338,37 +1292,20 @@ value_t value_t::annotated_date() const 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 annotated date of a string"); - case XML_NODE: return as_xml_node()->to_value().annotated_date(); - case POINTER: - throw_(value_error, "Cannot find the annotated date of a pointer"); - case SEQUENCE: - throw_(value_error, "Cannot find the annotated date of a sequence"); - default: - assert(false); break; } - assert(false); + + throw_(value_error, "Cannot find the annotated date of " << label()); return value_t(); } value_t value_t::annotated_tag() const { switch (type()) { - case BOOLEAN: - throw_(value_error, "Cannot find the annotated tag of a boolean"); - case INTEGER: - throw_(value_error, "Cannot find the annotated tag of an integer"); - case DATETIME: return *this; @@ -1379,26 +1316,14 @@ value_t value_t::annotated_tag() const return value_t(*temp, true); } - 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 annotated tag of a string"); - case XML_NODE: return as_xml_node()->to_value().annotated_tag(); - case POINTER: - throw_(value_error, "Cannot find the annotated tag of a pointer"); - case SEQUENCE: - throw_(value_error, "Cannot find the annotated tag of a sequence"); - default: - assert(false); break; } - assert(false); + + throw_(value_error, "Cannot find the annotated tag of " << label()); return value_t(); } @@ -1442,17 +1367,11 @@ value_t value_t::strip_annotations(const bool keep_price, value_t value_t::cost() const { switch (type()) { - case BOOLEAN: - throw_(value_error, "Cannot find the cost of a boolean"); - case INTEGER: case AMOUNT: case BALANCE: return *this; - case DATETIME: - throw_(value_error, "Cannot find the cost of a date/time"); - case BALANCE_PAIR: assert(as_balance_pair().cost); if (as_balance_pair().cost) @@ -1460,33 +1379,20 @@ value_t value_t::cost() const else return as_balance_pair().quantity; - case STRING: - throw_(value_error, "Cannot find the cost of a string"); - case XML_NODE: return as_xml_node()->to_value().cost(); - case POINTER: - throw_(value_error, "Cannot find the cost of a pointer"); - case SEQUENCE: - throw_(value_error, "Cannot find the cost of a sequence"); - default: - assert(false); break; } - assert(false); + + throw_(value_error, "Cannot find the cost of " << label()); return value_t(); } value_t& value_t::add(const amount_t& amount, const optional<amount_t>& tcost) { switch (type()) { - case BOOLEAN: - throw_(value_error, "Cannot add an amount to a boolean"); - case DATETIME: - throw_(value_error, "Cannot add an amount to a date/time"); - case INTEGER: case AMOUNT: if (tcost) { @@ -1517,20 +1423,11 @@ value_t& value_t::add(const amount_t& amount, const optional<amount_t>& tcost) as_balance_pair_lval().add(amount, tcost); break; - case STRING: - throw_(value_error, "Cannot add an amount to a string"); - case XML_NODE: - throw_(value_error, "Cannot add an amount to an XML node"); - case POINTER: - throw_(value_error, "Cannot add an amount to a pointer"); - case SEQUENCE: - throw_(value_error, "Cannot add an amount to a sequence"); - default: - assert(false); break; } + throw_(value_error, "Cannot add an amount to " << label()); return *this; } diff --git a/src/xpath.cc b/src/xpath.cc index f779ebc6..ed4c0544 100644 --- a/src/xpath.cc +++ b/src/xpath.cc @@ -1365,7 +1365,7 @@ bool xpath_t::op_t::print(std::ostream& out, print_context_t& context) const break; case FUNCTION: - out << '<FUNCTION>'; + out << "<FUNCTION>"; break; case ARG_INDEX: |