#ifndef _WALK_H #define _WALK_H #include "ledger.h" #include "balance.h" #include "valexpr.h" #include "datetime.h" #include #include namespace ledger { template struct item_handler { virtual ~item_handler() {} virtual void flush() {} virtual void operator()(T * item) = 0; }; template struct compare_items { const value_expr_t * sort_order; compare_items(const value_expr_t * _sort_order) : sort_order(_sort_order) { assert(sort_order); } bool operator()(const T * left, const T * right) const { assert(left); assert(right); balance_t left_result; sort_order->compute(left_result, details_t(left)); balance_t right_result; sort_order->compute(right_result, details_t(right)); return left_result < right_result; } }; ////////////////////////////////////////////////////////////////////// // // Transaction handlers // typedef std::deque transactions_deque; typedef std::deque entries_deque; struct ignore_transactions : public item_handler { virtual void operator()(transaction_t * xact) {} }; struct clear_display_flags : public item_handler { virtual void operator()(transaction_t * xact) { xact->dflags = 0; } }; class sort_transactions : public item_handler { transactions_deque transactions; const value_expr_t * sort_order; item_handler * handler; public: sort_transactions(item_handler * _handler, const value_expr_t * _sort_order) : sort_order(_sort_order), handler(_handler) {} virtual ~sort_transactions() { flush(); handler->flush(); delete handler; } virtual void flush() { std::stable_sort(transactions.begin(), transactions.end(), compare_items(sort_order)); for (transactions_deque::iterator i = transactions.begin(); i != transactions.end(); i++) (*handler)(*i); transactions.clear(); handler->flush(); } virtual void operator()(transaction_t * xact) { transactions.push_back(xact); } }; class filter_transactions : public item_handler { item_predicate pred; item_handler * handler; public: filter_transactions(item_handler * _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 * last_xact; const bool inverted; item_handler * handler; public: calc_transactions(item_handler * _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 { balance_pair_t subtotal; unsigned int count; entry_t * last_entry; transaction_t * last_xact; item_handler * handler; account_t * totals_account; transactions_deque xact_temps; public: collapse_transactions(item_handler * _handler) : count(0), last_entry(NULL), last_xact(NULL), handler(_handler) { totals_account = new account_t(NULL, ""); } 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 += *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 * last_xact; item_handler * handler; entries_deque entry_temps; transactions_deque xact_temps; public: changed_value_transactions(item_handler * _handler) : last_xact(NULL), handler(_handler) {} virtual ~changed_value_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() { (*this)(NULL); } virtual void operator()(transaction_t * xact); }; typedef std::map balances_map; typedef std::pair balances_pair; class subtotal_transactions : public item_handler { protected: std::time_t start; std::time_t finish; balances_map balances; item_handler * handler; entries_deque entry_temps; transactions_deque xact_temps; public: subtotal_transactions(item_handler * _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 subtotal_transactions { std::time_t begin; interval_t interval; transaction_t * last_xact; public: interval_transactions(item_handler * _handler, std::time_t _begin, const interval_t& _interval) : subtotal_transactions(_handler), begin(_begin), interval(_interval), last_xact(NULL) {} virtual ~interval_transactions() { start = begin; finish = interval.increment(begin); } virtual void operator()(transaction_t * xact) { if (std::difftime(xact->entry->date, interval.increment(begin)) > 0) { if (last_xact) { start = begin; finish = interval.increment(begin); flush(); } begin = interval.increment(begin); std::time_t temp; while (std::difftime(xact->entry->date, temp = interval.increment(begin)) > 0) begin = temp; } subtotal_transactions::operator()(xact); last_xact = xact; } }; class related_transactions : public item_handler { bool also_matching; item_handler * handler; public: related_transactions(item_handler * _handler, bool _also_matching = false) : also_matching(_also_matching), handler(_handler) {} virtual ~related_transactions() { handler->flush(); delete handler; } virtual void operator()(transaction_t * xact) { for (transactions_list::iterator i = xact->entry->transactions.begin(); i != xact->entry->transactions.end(); i++) if (! ((*i)->dflags & TRANSACTION_HANDLED) && (*i == xact ? also_matching : ! ((*i)->flags & TRANSACTION_AUTO))) { (*i)->dflags |= TRANSACTION_HANDLED; (*handler)(*i); } } }; ////////////////////////////////////////////////////////////////////// inline void walk_transactions(transactions_list::iterator begin, transactions_list::iterator end, item_handler& handler) { for (transactions_list::iterator i = begin; i != end; i++) handler(*i); } inline void walk_transactions(transactions_list& list, item_handler& handler) { walk_transactions(list.begin(), list.end(), handler); } inline void walk_transactions(transactions_deque::iterator begin, transactions_deque::iterator end, item_handler& handler) { for (transactions_deque::iterator i = begin; i != end; i++) handler(*i); } inline void walk_transactions(transactions_deque& deque, item_handler& handler) { walk_transactions(deque.begin(), deque.end(), handler); } inline void walk_entries(entries_list::iterator begin, entries_list::iterator end, item_handler& handler) { // jww (2004-08-11): do transaction dflags need to be cleared first? for (entries_list::iterator i = begin; i != end; i++) walk_transactions((*i)->transactions, handler); } inline void walk_entries(entries_list& list, item_handler& handler) { walk_entries(list.begin(), list.end(), handler); } ////////////////////////////////////////////////////////////////////// // // Account handlers // typedef std::deque accounts_deque; struct add_to_account_value : public item_handler { virtual void operator()(transaction_t * xact) { xact->account->value += *xact; } }; #if 0 class format_accounts : public item_handler { }; class filter_accounts : public item_handler { item_handler * handler; public: filter_accounts(item_handler * _handler) : handler(_handler) {} virtual ~filter_accounts() { handler->flush(); delete handler; } virtual void flush() {} virtual void operator()(account_t * account) { } }; class sort_accounts : public item_handler { value_expr_t * sort_order; item_handler * handler; public: sort_accounts(item_handler * _handler, value_expr_t * _sort_order) : sort_order(_sort_order), handler(_handler) {} virtual ~sort_accounts() { handler->flush(); delete handler; } virtual void flush() {} virtual void operator()(account_t * account) { accounts_deque accounts; for (accounts_map::iterator i = account->accounts.begin(); i != account->accounts.end(); i++) accounts.push_back((*i).second); std::stable_sort(accounts.begin(), accounts.end(), compare_items(sort_order)); } }; class balance_accounts : public item_handler { item_handler * handler; public: balance_accounts(item_handler * _handler) : handler(_handler) {} virtual ~balance_accounts() { handler->flush(); delete handler; } virtual void flush() { if (format_account::disp_subaccounts_p(top)) { std::string end_format = "--------------------\n"; format.reset(end_format + f); format.format_elements(std::cout, details_t(top)); } } virtual void operator()(account_t * account) { } }; #endif ////////////////////////////////////////////////////////////////////// inline void sum_accounts(account_t * account) { for (accounts_map::iterator i = account->accounts.begin(); i != account->accounts.end(); i++) { sum_accounts((*i).second); account->total += (*i).second->total; } account->total += account->value; } inline void sort_accounts(account_t * account, accounts_deque& accounts, const value_expr_t * sort_order) { for (accounts_map::iterator i = account->accounts.begin(); i != account->accounts.end(); i++) accounts.push_back((*i).second); std::stable_sort(accounts.begin(), accounts.end(), compare_items(sort_order)); } inline void walk_accounts(account_t * account, item_handler& handler, const value_expr_t * sort_order) { handler(account); accounts_deque accounts; sort_accounts(account, accounts, sort_order); for (accounts_deque::const_iterator i = accounts.begin(); i != accounts.end(); i++) walk_accounts(*i, handler, sort_order); } inline void walk_accounts(account_t * account, item_handler& handler) { handler(account); for (accounts_map::const_iterator i = account->accounts.begin(); i != account->accounts.end(); i++) walk_accounts((*i).second, handler); } } // namespace ledger #endif // _WALK_H