#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 { item_handler * handler; public: item_handler() : handler(NULL) { DEBUG_PRINT("ledger.memory.ctors", "ctor item_handler"); } item_handler(item_handler * _handler) : handler(_handler) { DEBUG_PRINT("ledger.memory.ctors", "ctor item_handler"); } virtual ~item_handler() { DEBUG_PRINT("ledger.memory.dtors", "dtor item_handler"); } virtual void flush() { if (handler) handler->flush(); } virtual void operator()(T& item) { if (handler) (*handler)(item); } }; template class compare_items { value_t left_result; value_t right_result; const value_expr_t * sort_order; public: compare_items(const value_expr_t * _sort_order) : sort_order(_sort_order) { assert(sort_order); } bool operator()(const T * left, const T * right) { assert(left); assert(right); sort_order->compute(left_result, details_t(*left)); sort_order->compute(right_result, details_t(*right)); return left_result < right_result; } }; ////////////////////////////////////////////////////////////////////// // // Transaction handlers // #define TRANSACTION_HANDLED 0x0001 #define TRANSACTION_DISPLAYED 0x0002 #define TRANSACTION_NO_TOTAL 0x0004 struct transaction_xdata_t { value_t total; unsigned int index; unsigned short dflags; transaction_xdata_t() : index(0), dflags(0) { DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_xdata_t"); } #ifdef DEBUG_ENABLED ~transaction_xdata_t() { DEBUG_PRINT("ledger.memory.dtors", "dtor transaction_xdata_t"); } #endif }; inline bool transaction_has_xdata(const transaction_t& xact) { return xact.data != NULL; } extern std::list transactions_xdata; inline transaction_xdata_t& transaction_xdata(const transaction_t& xact) { if (! xact.data) { transactions_xdata.push_back(transaction_xdata_t()); xact.data = &transactions_xdata.back(); } return *((transaction_xdata_t *) xact.data); } ////////////////////////////////////////////////////////////////////// typedef std::deque transactions_deque; typedef std::deque entries_deque; 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); transactions_xdata.clear(); } 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); transactions_xdata.clear(); } 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) { 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); } ////////////////////////////////////////////////////////////////////// class ignore_transactions : public item_handler { public: virtual void operator()(transaction_t& xact) {} }; class set_account_value : public item_handler { public: set_account_value(item_handler * handler = NULL) : item_handler(handler) {} virtual void operator()(transaction_t& xact); }; class sort_transactions : public item_handler { transactions_deque transactions; const value_expr_t * sort_order; bool allocated; public: sort_transactions(item_handler * handler, const value_expr_t * _sort_order) : item_handler(handler), sort_order(_sort_order), allocated(false) {} sort_transactions(item_handler * handler, const std::string& _sort_order) : item_handler(handler), sort_order(parse_value_expr(_sort_order)), allocated(true) {} virtual ~sort_transactions() { assert(sort_order); if (allocated) delete sort_order; } virtual void flush(); virtual void operator()(transaction_t& xact) { transactions.push_back(&xact); } }; class filter_transactions : public item_handler { const item_predicate * pred; bool allocated; public: filter_transactions(item_handler * handler, const item_predicate * predicate) : item_handler(handler), pred(predicate), allocated(false) {} filter_transactions(item_handler * handler, const std::string& predicate) : item_handler(handler), pred(new item_predicate(predicate)), allocated(true) {} virtual ~filter_transactions() { if (allocated) delete pred; } virtual void operator()(transaction_t& xact) { if ((*pred)(xact)) (*handler)(xact); } }; class calc_transactions : public item_handler { transaction_t * last_xact; const bool inverted; public: calc_transactions(item_handler * handler, const bool _inverted = false) : item_handler(handler), last_xact(NULL), inverted(_inverted) {} 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; account_t totals_account; std::list xact_temps; public: collapse_transactions(item_handler * handler) : item_handler(handler), count(0), last_entry(NULL), last_xact(NULL), totals_account(NULL, "") {} virtual void flush() { if (subtotal) report_cumulative_subtotal(); item_handler::flush(); } 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(); add_transaction_to(xact, subtotal); count++; last_entry = xact.entry; last_xact = &xact; } }; class changed_value_transactions : public item_handler { // This filter requires that calc_transactions be used at some point // later in the chain. bool changed_values_only; transaction_t * last_xact; std::list entry_temps; std::list xact_temps; public: changed_value_transactions(item_handler * handler, bool _changed_values_only) : item_handler(handler), changed_values_only(_changed_values_only), last_xact(NULL) {} virtual void flush() { output_diff(std::time(NULL)); last_xact = NULL; item_handler::flush(); } void output_diff(const std::time_t current); virtual void operator()(transaction_t& xact); }; class subtotal_transactions : public item_handler { typedef std::map balances_map; typedef std::pair balances_pair; protected: std::time_t start; std::time_t finish; balances_map balances; std::list entry_temps; std::list xact_temps; public: subtotal_transactions(item_handler * handler) : item_handler(handler) {} void flush(const char * spec_fmt); virtual void flush() { flush(NULL); } virtual void operator()(transaction_t& xact); }; class interval_transactions : public subtotal_transactions { interval_t interval; transaction_t * last_xact; public: interval_transactions(item_handler * handler, const interval_t& _interval) : subtotal_transactions(handler), interval(_interval), last_xact(NULL) {} interval_transactions(item_handler * handler, const std::string& _interval) : subtotal_transactions(handler), interval(_interval), last_xact(NULL) {} virtual ~interval_transactions() { start = interval.begin; finish = interval.increment(interval.begin); } virtual void operator()(transaction_t& xact); }; class dow_transactions : public subtotal_transactions { transactions_list days_of_the_week[7]; public: dow_transactions(item_handler * handler) : subtotal_transactions(handler) {} virtual void flush(); virtual void operator()(transaction_t& xact) { struct std::tm * desc = std::localtime(&xact.entry->date); days_of_the_week[desc->tm_wday].push_back(&xact); } }; class related_transactions : public item_handler { bool also_matching; public: related_transactions(item_handler * handler, const bool _also_matching = false) : item_handler(handler), also_matching(_also_matching) {} virtual void operator()(transaction_t& xact) { for (transactions_list::iterator i = xact.entry->transactions.begin(); i != xact.entry->transactions.end(); i++) if ((! transaction_has_xdata(**i) || ! (transaction_xdata(**i).dflags & TRANSACTION_HANDLED)) && (*i == &xact ? also_matching : ! ((*i)->flags & TRANSACTION_AUTO))) { transaction_xdata(**i).dflags |= TRANSACTION_HANDLED; (*handler)(**i); } } }; ////////////////////////////////////////////////////////////////////// // // Account walking functions // #define ACCOUNT_DISPLAYED 0x1 #define ACCOUNT_TO_DISPLAY 0x2 struct account_xdata_t { value_t value; value_t total; unsigned int count; // transactions counted toward total unsigned int subcount; unsigned short dflags; account_xdata_t() : count(0), subcount(0), dflags(0) { DEBUG_PRINT("ledger.memory.ctors", "ctor account_xdata_t"); } #ifdef DEBUG_ENABLED ~account_xdata_t() { DEBUG_PRINT("ledger.memory.dtors", "dtor account_xdata_t"); } #endif }; inline bool account_has_xdata(const account_t& account) { return account.data != NULL; } extern std::list accounts_xdata; inline account_xdata_t& account_xdata(const account_t& account) { if (! account.data) { accounts_xdata.push_back(account_xdata_t()); account.data = &accounts_xdata.back(); } return *((account_xdata_t *) account.data); } inline void set_account_value::operator()(transaction_t& xact) { add_transaction_to(xact, account_xdata(*xact.account).value); account_xdata(*xact.account).subcount++; if (handler) (*handler)(xact); } ////////////////////////////////////////////////////////////////////// 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_xdata(account).total += account_xdata(*(*i).second).total; account_xdata(account).count += (account_xdata(*(*i).second).count + account_xdata(*(*i).second).subcount); } account_xdata(account).total += account_xdata(account).value; account_xdata(account).count += account_xdata(account).subcount; } typedef std::deque accounts_deque; inline void sort_accounts(account_t& account, const value_expr_t * sort_order, 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)); } inline void walk_accounts(account_t& account, item_handler& handler, const value_expr_t * sort_order = NULL) { handler(account); if (sort_order) { accounts_deque accounts; sort_accounts(account, sort_order, accounts); for (accounts_deque::const_iterator i = accounts.begin(); i != accounts.end(); i++) walk_accounts(**i, handler, sort_order); } else { for (accounts_map::const_iterator i = account.accounts.begin(); i != account.accounts.end(); i++) walk_accounts(*(*i).second, handler); } accounts_xdata.clear(); } inline void walk_accounts(account_t& account, item_handler& handler, const std::string& sort_string) { std::auto_ptr sort_order(parse_value_expr(sort_string)); walk_accounts(account, handler, sort_order.get()); } } // namespace ledger #endif // _WALK_H