diff options
-rw-r--r-- | format.cc | 93 | ||||
-rw-r--r-- | format.h | 143 | ||||
-rw-r--r-- | main.cc | 28 | ||||
-rw-r--r-- | walk.cc | 146 | ||||
-rw-r--r-- | walk.h | 253 |
5 files changed, 427 insertions, 236 deletions
@@ -312,99 +312,6 @@ void format_t::format_elements(std::ostream& out, } } -#ifdef COLLAPSED_REGISTER - -void format_transaction::report_cumulative_subtotal() const -{ - if (count == 1) { - first_line_format.format_elements(output_stream, details_t(last_xact)); - return; - } - - assert(count > 1); - - account_t splits(NULL, "<Total>"); - transaction_t splits_total(NULL, &splits); - splits_total.total = subtotal; - - balance_t value; - format_t::compute_total(value, details_t(&splits_total)); - - splits_total.entry = last_entry; - splits_total.total = last_xact->total; - - bool first = true; - for (amounts_map::const_iterator i = value.amounts.begin(); - i != value.amounts.end(); - i++) { - splits_total.amount = (*i).second; - splits_total.cost = (*i).second; - splits_total.total += (*i).second; - if (first) { - first_line_format.format_elements(output_stream, - details_t(&splits_total)); - first = false; - } else { - next_lines_format.format_elements(output_stream, - details_t(&splits_total)); - } - } -} - -#endif // COLLAPSED_REGISTER - -void format_transaction::operator()(transaction_t * xact) const -{ - if (last_xact) - xact->total += last_xact->total; - - if (inverted) { - xact->amount.negate(); - xact->cost.negate(); - } - - xact->total += *xact; - xact->index = last_xact ? last_xact->index + 1 : 0; - - if (disp_pred(xact)) { - xact->dflags |= TRANSACTION_DISPLAYED; - - // This makes the assumption that transactions from a single entry - // are always grouped together. - -#ifdef COLLAPSED_REGISTER - if (collapsed) { - // If we've reached a new entry, report on the subtotal - // accumulated thus far. - - if (last_entry && last_entry != xact->entry) { - report_cumulative_subtotal(); - subtotal = 0; - count = 0; - } - - subtotal += *xact; - count++; - } else -#endif - { - if (last_entry != xact->entry) { - first_line_format.format_elements(output_stream, details_t(xact)); - } else { - next_lines_format.format_elements(output_stream, details_t(xact)); - } - } - } - - if (inverted) { - xact->amount.negate(); - xact->cost.negate(); - } - - last_entry = xact->entry; - last_xact = xact; -} - bool format_account::disp_subaccounts_p(const account_t * account, const item_predicate<account_t>& disp_pred, @@ -83,133 +83,45 @@ struct format_t } }; -template <typename T> -struct item_formatter : public item_handler<T> { - virtual ~item_formatter() {} - virtual void flush() const = 0; -}; - -#define COLLAPSED_REGISTER 1 // support collapsed registers - -class format_transaction : public item_formatter<transaction_t> +class format_transactions : public item_handler<transaction_t> { std::ostream& output_stream; const format_t& first_line_format; const format_t& next_lines_format; -#ifdef COLLAPSED_REGISTER - const bool collapsed; -#endif - const bool inverted; - - item_predicate<transaction_t> disp_pred; - -#ifdef COLLAPSED_REGISTER - mutable balance_pair_t subtotal; - mutable unsigned int count; -#endif - mutable entry_t * last_entry; - mutable transaction_t * last_xact; + entry_t * last_entry; public: - format_transaction(std::ostream& _output_stream, - const format_t& _first_line_format, - const format_t& _next_lines_format, - const std::string& display_predicate = NULL, -#ifdef COLLAPSED_REGISTER - const bool _collapsed = false, -#endif - const bool _inverted = false) + format_transactions(std::ostream& _output_stream, + const format_t& _first_line_format, + const format_t& _next_lines_format) : output_stream(_output_stream), first_line_format(_first_line_format), - next_lines_format(_next_lines_format), -#ifdef COLLAPSED_REGISTER - collapsed(_collapsed), -#endif - inverted(_inverted), disp_pred(display_predicate), -#ifdef COLLAPSED_REGISTER - count(0), -#endif - last_entry(NULL), last_xact(NULL) {} - -#ifdef COLLAPSED_REGISTER - virtual ~format_transaction() { - flush(); - } - - virtual void flush() const { - if (subtotal) - report_cumulative_subtotal(); - output_stream.flush(); - } - - void report_cumulative_subtotal() const; -#endif - - virtual void operator()(transaction_t * xact) const; -}; - - -class changed_value_filter : public item_formatter<transaction_t> -{ - item_formatter<transaction_t> * handler; + next_lines_format(_next_lines_format), last_entry(NULL) {} - mutable entry_t modified_entry; - mutable transaction_t modified_xact; - mutable transaction_t * last_xact; - - public: - changed_value_filter(item_formatter<transaction_t> * _handler) - : handler(_handler), modified_xact(&modified_entry, NULL), - last_xact(NULL) { - assert(handler); - modified_entry.payee = "Commodities revalued"; - } - - virtual ~changed_value_filter() { + virtual ~format_transactions() { flush(); - handler->flush(); - assert(handler); - delete handler; } - virtual void flush() const { - (*this)(NULL); + virtual void flush() { + output_stream.flush(); } - virtual void operator()(transaction_t * xact) const { - if (last_xact) { - balance_t prev_bal, cur_bal; - - format_t::compute_total(prev_bal, details_t(last_xact)); - - std::time_t current = xact ? xact->entry->date : std::time(NULL); - std::time_t prev_date = last_xact->entry->date; - last_xact->entry->date = current; - format_t::compute_total(cur_bal, details_t(last_xact)); - last_xact->entry->date = prev_date; - - if (balance_t diff = cur_bal - prev_bal) { - modified_entry.date = current; + virtual void operator()(transaction_t * xact) { + xact->dflags |= TRANSACTION_DISPLAYED; - // jww (2004-08-07): What if there are multiple commodities? - assert(diff.amounts.size() == 1); - modified_xact.amount = diff.amount(); - modified_xact.total = diff; - modified_xact.total.negate(); + // This makes the assumption that transactions from a single entry + // are always grouped together. - (*handler)(&modified_xact); - } - } - - if (xact) - (*handler)(xact); + if (last_entry != xact->entry) + first_line_format.format_elements(output_stream, details_t(xact)); + else + next_lines_format.format_elements(output_stream, details_t(xact)); - last_xact = xact; + last_entry = xact->entry; } }; - -class format_account : public item_formatter<account_t> +class format_account : public item_handler<account_t> { std::ostream& output_stream; const format_t& format; @@ -222,10 +134,9 @@ class format_account : public item_formatter<account_t> const std::string& display_predicate = NULL) : output_stream(_output_stream), format(_format), disp_pred(display_predicate) {} - virtual ~format_account() {} - virtual void flush() const { - output_stream.flush(); + virtual ~format_account() { + flush(); } static bool disp_subaccounts_p(const account_t * account, @@ -239,7 +150,11 @@ class format_account : public item_formatter<account_t> static bool display_account(const account_t * account, const item_predicate<account_t>& disp_pred); - virtual void operator()(account_t * account) const { + virtual void flush() { + output_stream.flush(); + } + + virtual void operator()(account_t * account) { if (display_account(account, disp_pred)) { format.format_elements(output_stream, details_t(account)); account->dflags |= ACCOUNT_DISPLAYED; @@ -248,7 +163,7 @@ class format_account : public item_formatter<account_t> }; -class format_equity : public item_formatter<account_t> +class format_equity : public item_handler<account_t> { std::ostream& output_stream; const format_t& first_line_format; @@ -277,14 +192,14 @@ class format_equity : public item_formatter<account_t> flush(); } - virtual void flush() const { + virtual void flush() { account_t summary(NULL, "Equity:Opening Balances"); summary.value = - total; next_lines_format.format_elements(output_stream, details_t(&summary)); output_stream.flush(); } - virtual void operator()(account_t * account) const { + virtual void operator()(account_t * account) { if (format_account::display_account(account, disp_pred)) { next_lines_format.format_elements(output_stream, details_t(account)); account->dflags |= ACCOUNT_DISPLAYED; @@ -607,7 +607,7 @@ int main(int argc, char * argv[]) xact_display_flags, true, sort_order.get()); } else if (command == "e") { - format_transaction formatter(std::cout, format, nformat); + format_transactions formatter(std::cout, format, nformat); for (transactions_list::iterator i = new_entry->transactions.begin(); i != new_entry->transactions.end(); @@ -615,24 +615,32 @@ int main(int argc, char * argv[]) handle_transaction(*i, formatter, xact_display_flags); } else { - std::auto_ptr<item_formatter<transaction_t> > - formatter(new format_transaction(std::cout, format, nformat, - display_predicate, -#ifdef COLLAPSED_REGISTER - ! show_subtotals, + std::auto_ptr<item_handler<transaction_t> > + formatter(new format_transactions(std::cout, format, nformat)); + + formatter.reset(new filter_transactions(formatter.release(), + display_predicate)); + formatter.reset(new calc_transactions(formatter.release(), + show_inverted)); + if (! show_subtotals) + formatter.reset(new collapse_transactions(formatter.release())); + if (show_expanded) + formatter.reset(new subtotal_transactions(formatter.release())); +#if 0 + formatter.reset(new interval_transactions(formatter.release(), + 0, 0, 9676800)); #endif - show_inverted)); if (show_commodities_revalued) - formatter.reset(new changed_value_filter(formatter.release())); + formatter.reset(new changed_value_transactions(formatter.release())); if (! sort_order.get()) { walk_entries(journal->entries.begin(), journal->entries.end(), *formatter.get(), predicate, xact_display_flags); } else { transactions_deque transactions_pool; + collect_transactions handler(transactions_pool); walk_entries(journal->entries.begin(), journal->entries.end(), - collect_transactions(transactions_pool), predicate, - xact_display_flags); + handler, predicate, xact_display_flags); std::stable_sort(transactions_pool.begin(), transactions_pool.end(), compare_items<transaction_t>(sort_order.get())); walk_transactions(transactions_pool.begin(), transactions_pool.end(), @@ -1,9 +1,151 @@ #include "walk.h" +#include "format.h" namespace ledger { -struct sum_in_account : public item_handler<transaction_t> { - virtual void operator()(transaction_t * xact) const { +void calc_transactions::operator()(transaction_t * xact) +{ + if (last_xact) + xact->total += last_xact->total; + + if (inverted) { + xact->amount.negate(); + xact->cost.negate(); + } + + xact->total += *xact; + xact->index = last_xact ? last_xact->index + 1 : 0; + + (*handler)(xact); + + if (inverted) { + xact->amount.negate(); + xact->cost.negate(); + } + + last_xact = xact; +} + +void collapse_transactions::report_cumulative_subtotal() +{ + if (count == 1) { + (*handler)(last_xact); + return; + } + + assert(count > 1); + + transaction_t * total_xact = new transaction_t(NULL, totals_account); + + balance_t value; + total_xact->total = subtotal; + format_t::compute_total(value, details_t(total_xact)); + total_xact->total = 0; + + total_xact->entry = last_entry; + + for (amounts_map::const_iterator i = value.amounts.begin(); + i != value.amounts.end(); + i++) { + total_xact->amount = (*i).second; + total_xact->cost = (*i).second; + + (*handler)(total_xact); + } + + xact_temps.push_back(total_xact); +} + +void changed_value_transactions::operator()(transaction_t * xact) +{ + if (last_xact) { + balance_t prev_bal; + balance_t cur_bal; + std::time_t current = xact ? xact->entry->date : std::time(NULL); + std::time_t prev_date = last_xact->entry->date; + + format_t::compute_total(prev_bal, details_t(last_xact)); + + last_xact->entry->date = current; + format_t::compute_total(cur_bal, details_t(last_xact)); + last_xact->entry->date = prev_date; + + if (balance_t diff = cur_bal - prev_bal) { + modified_entry.date = current; + + // jww (2004-08-07): What if there are multiple commodities? + assert(diff.amounts.size() == 1); + modified_xact.amount = diff.amount(); + modified_xact.total = diff; + modified_xact.total.negate(); + + (*handler)(&modified_xact); + } + } + + if (xact) + (*handler)(xact); + + last_xact = xact; +} + +void subtotal_transactions::flush() +{ + entry_t * entry = new entry_t; + entry->date = start; + + char buf[256]; + // jww (2004-08-10): allow for a format string here + std::strftime(buf, 255, "- %Y/%m/%d", std::gmtime(&finish)); + entry->payee = buf; + + entry_temps.push_back(entry); + + for (balances_map::iterator i = balances.begin(); + i != balances.end(); + i++) { + transaction_t * xact = new transaction_t(entry, (*i).first); + xact->total = (*i).second; + balance_t result; + format_t::compute_total(result, details_t(xact)); + xact->total = 0; + + for (amounts_map::const_iterator j = result.amounts.begin(); + j != result.amounts.end(); + j++) { + xact->amount = (*j).second; + xact->cost = (*j).second; + + (*handler)(xact); + } + + xact_temps.push_back(xact); + } + + balances.clear(); +} + +void subtotal_transactions::operator()(transaction_t * xact) +{ + if (balances.size() == 0) { + start = finish = xact->entry->date; + } else { + if (std::difftime(xact->entry->date, start) < 0) + start = xact->entry->date; + if (std::difftime(xact->entry->date, finish) > 0) + finish = xact->entry->date; + } + + balances_map::iterator i = balances.find(xact->account); + if (i == balances.end()) + balances.insert(balances_pair(xact->account, *xact)); + else + (*i).second += *xact; +} + +struct sum_in_account : public item_handler<transaction_t> +{ + virtual void operator()(transaction_t * xact) { xact->account->value += *xact; } }; @@ -13,7 +13,8 @@ namespace ledger { template <typename T> struct item_handler { virtual ~item_handler() {} - virtual void operator()(T * item) const = 0; + virtual void flush() {} + virtual void operator()(T * item) = 0; }; template <typename T> @@ -36,16 +37,28 @@ struct compare_items { } }; +////////////////////////////////////////////////////////////////////// +// +// Several default handlers +// + typedef std::deque<transaction_t *> transactions_deque; +typedef std::deque<entry_t *> entries_deque; + +struct ignore_transaction : public item_handler<transaction_t> +{ + virtual void operator()(transaction_t * xact) {} +}; -class collect_transactions : public item_handler<transaction_t> { +class collect_transactions : public item_handler<transaction_t> +{ transactions_deque& transactions; public: collect_transactions(transactions_deque& _transactions) : transactions(_transactions) {} - virtual void operator()(transaction_t * xact) const { + virtual void operator()(transaction_t * xact) { transactions.push_back(xact); } }; @@ -57,15 +70,219 @@ inline void sort_transactions(transactions_deque& transactions, compare_items<transaction_t>(sort_order)); } -struct ignore_transaction : public item_handler<transaction_t> { - virtual void operator()(transaction_t * xact) const {} +class filter_transactions : public item_handler<transaction_t> +{ + item_predicate<transaction_t> pred; + + item_handler<transaction_t> * handler; + + public: + filter_transactions(item_handler<transaction_t> * _handler, + const std::string& predicate) + : pred(predicate), handler(_handler) {} + + virtual ~filter_transactions() { + handler->flush(); + delete handler; + } + + virtual void operator()(transaction_t * xact) { + if (pred(xact)) + (*handler)(xact); + } }; +class calc_transactions : public item_handler<transaction_t> +{ + transaction_t * last_xact; + const bool inverted; + + item_handler<transaction_t> * handler; + + public: + calc_transactions(item_handler<transaction_t> * _handler, + const bool _inverted = false) + : last_xact(NULL), inverted(_inverted), handler(_handler) {} + + virtual ~calc_transactions() { + handler->flush(); + delete handler; + } + + virtual void operator()(transaction_t * xact); +}; + +class collapse_transactions : public item_handler<transaction_t> +{ + balance_pair_t subtotal; + unsigned int count; + entry_t * last_entry; + transaction_t * last_xact; + + item_handler<transaction_t> * handler; + + account_t * totals_account; + transactions_deque xact_temps; + + public: + collapse_transactions(item_handler<transaction_t> * _handler) + : count(0), last_entry(NULL), last_xact(NULL), + handler(_handler) { + totals_account = new account_t(NULL, "<Total>"); + } + + virtual ~collapse_transactions() { + flush(); + handler->flush(); + + delete handler; + delete totals_account; + + for (transactions_deque::iterator i = xact_temps.begin(); + i != xact_temps.end(); + i++) + delete *i; + } + + virtual void flush() { + if (subtotal) + report_cumulative_subtotal(); + } + + void report_cumulative_subtotal(); + + virtual void operator()(transaction_t * xact) { + // If we've reached a new entry, report on the subtotal + // accumulated thus far. + + if (last_entry && last_entry != xact->entry) { + report_cumulative_subtotal(); + subtotal = 0; + count = 0; + } + + subtotal += *xact; + count++; + + last_entry = xact->entry; + last_xact = xact; + } +}; + +// This filter requires that calc_transactions be used. + +class changed_value_transactions : public item_handler<transaction_t> +{ + entry_t modified_entry; + transaction_t modified_xact; + transaction_t * last_xact; + + item_handler<transaction_t> * handler; + + public: + changed_value_transactions(item_handler<transaction_t> * _handler) + : modified_xact(&modified_entry, NULL), last_xact(NULL), + handler(_handler) { + assert(handler); + modified_entry.payee = "Commodities revalued"; + } + + virtual ~changed_value_transactions() { + flush(); + handler->flush(); + delete handler; + } + + virtual void flush() { + (*this)(NULL); + } + + virtual void operator()(transaction_t * xact); +}; + +typedef std::map<account_t *, balance_pair_t> balances_map; +typedef std::pair<account_t *, balance_pair_t> balances_pair; + +class subtotal_transactions : public item_handler<transaction_t> +{ + std::time_t start; + std::time_t finish; + balances_map balances; + + item_handler<transaction_t> * handler; + + entries_deque entry_temps; + transactions_deque xact_temps; + + public: + subtotal_transactions(item_handler<transaction_t> * _handler) + : handler(_handler) {} + + virtual ~subtotal_transactions() { + flush(); + handler->flush(); + delete handler; + + for (entries_deque::iterator i = entry_temps.begin(); + i != entry_temps.end(); + i++) + delete *i; + + for (transactions_deque::iterator i = xact_temps.begin(); + i != xact_temps.end(); + i++) + delete *i; + } + + virtual void flush(); + virtual void operator()(transaction_t * xact); +}; + +class interval_transactions : public item_handler<transaction_t> +{ + std::time_t start; + unsigned long months; + unsigned long seconds; + transaction_t * last_xact; + + item_handler<transaction_t> * handler; + + public: + interval_transactions(item_handler<transaction_t> * _handler, + std::time_t _start, unsigned long _months, + unsigned long _seconds) + : start(_start), months(_months), seconds(_seconds), + last_xact(NULL), handler(_handler) {} + + virtual ~interval_transactions() { + flush(); + } + + virtual void flush() { + handler->flush(); + } + virtual void operator()(transaction_t * xact) { + if (std::difftime(xact->entry->date, start + seconds) > 0) { + if (last_xact) + handler->flush(); + start += seconds; + while (std::difftime(xact->entry->date, start + seconds) > 0) + start += seconds; + } + + (*handler)(xact); + + last_xact = xact; + } +}; + +////////////////////////////////////////////////////////////////////// + #define MATCHING_TRANSACTIONS 0x01 #define OTHER_TRANSACTIONS 0x02 inline void handle_transaction(transaction_t * xact, - const item_handler<transaction_t>& handler, + item_handler<transaction_t>& handler, unsigned int flags) { for (transactions_list::iterator i = xact->entry->transactions.begin(); @@ -83,7 +300,7 @@ inline void handle_transaction(transaction_t * xact, inline void walk_entries(entries_list::iterator begin, entries_list::iterator end, - const item_handler<transaction_t>& handler, + item_handler<transaction_t>& handler, const std::string& predicate, unsigned int flags) { @@ -99,7 +316,7 @@ inline void walk_entries(entries_list::iterator begin, inline void walk_entries(entries_list::iterator begin, entries_list::iterator end, - const item_handler<transaction_t>& handler) + item_handler<transaction_t>& handler) { for (entries_list::iterator i = begin; i != end; i++) for (transactions_list::iterator j = (*i)->transactions.begin(); @@ -108,8 +325,9 @@ inline void walk_entries(entries_list::iterator begin, handler(*j); } -struct clear_flags : public item_handler<transaction_t> { - virtual void operator()(transaction_t * xact) const { +struct clear_flags : public item_handler<transaction_t> +{ + virtual void operator()(transaction_t * xact) { xact->dflags = 0; } }; @@ -117,12 +335,13 @@ struct clear_flags : public item_handler<transaction_t> { inline void clear_transaction_display_flags(entries_list::iterator begin, entries_list::iterator end) { - walk_entries(begin, end, clear_flags()); + clear_flags handler; + walk_entries(begin, end, handler); } inline void walk_transactions(transactions_list::iterator begin, transactions_list::iterator end, - const item_handler<transaction_t>& handler) + item_handler<transaction_t>& handler) { for (transactions_list::iterator i = begin; i != end; i++) handler(*i); @@ -130,7 +349,7 @@ inline void walk_transactions(transactions_list::iterator begin, inline void walk_transactions(transactions_deque::iterator begin, transactions_deque::iterator end, - const item_handler<transaction_t>& handler) + item_handler<transaction_t>& handler) { for (transactions_deque::iterator i = begin; i != end; i++) handler(*i); @@ -152,7 +371,7 @@ inline void sort_accounts(account_t * account, } inline void walk__accounts(account_t * account, - const item_handler<account_t>& handler) + item_handler<account_t>& handler) { handler(account); @@ -163,7 +382,7 @@ inline void walk__accounts(account_t * account, } inline void walk__accounts_sorted(account_t * account, - const item_handler<account_t>& handler, + item_handler<account_t>& handler, const node_t * sort_order) { handler(account); @@ -185,7 +404,7 @@ inline void walk__accounts_sorted(account_t * account, } inline void for_each_account(account_t * account, - const item_handler<account_t>& handler) + item_handler<account_t>& handler) { handler(account); @@ -211,7 +430,7 @@ inline void sum__accounts(account_t * account) } inline void walk_accounts(account_t * account, - const item_handler<account_t>& handler, + item_handler<account_t>& handler, const std::string& predicate, unsigned int flags, const bool calc_subtotals, |