#ifndef _WALK_H #define _WALK_H #include "journal.h" #include "balance.h" #include "valexpr.h" #include #include #include namespace ledger { template struct item_handler { item_handler * handler; public: item_handler() : handler(NULL) { DEBUG("ledger.memory.ctors", "ctor item_handler"); } item_handler(item_handler * _handler) : handler(_handler) { DEBUG("ledger.memory.ctors", "ctor item_handler"); } virtual ~item_handler() { DEBUG("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_expr sort_order; public: compare_items(value_expr _sort_order) : sort_order(_sort_order) {} bool operator()(const T * left, const T * right); }; template bool compare_items::operator()(const T * left, const T * right) { assert(left); assert(right); value_t left_result; value_t right_result; sort_order.compute(left_result, details_t(*left)); sort_order.compute(right_result, details_t(*right)); return left_result < right_result; } template <> bool compare_items::operator()(const transaction_t * left, const transaction_t * right); template <> bool compare_items::operator()(const account_t * left, const account_t * right); ////////////////////////////////////////////////////////////////////// // // Transaction handlers // #define TRANSACTION_RECEIVED 0x0001 #define TRANSACTION_HANDLED 0x0002 #define TRANSACTION_TO_DISPLAY 0x0004 #define TRANSACTION_DISPLAYED 0x0008 #define TRANSACTION_NO_TOTAL 0x0010 #define TRANSACTION_SORT_CALC 0x0020 #define TRANSACTION_COMPOUND 0x0040 #define TRANSACTION_MATCHES 0x0080 struct transaction_xdata_t { value_t total; value_t sort_value; value_t value; unsigned int index; unsigned short dflags; datetime_t date; account_t * account; void * ptr; transactions_list * component_xacts; transaction_xdata_t() : index(0), dflags(0), account(NULL), ptr(NULL), component_xacts(NULL) { DEBUG("ledger.memory.ctors", "ctor transaction_xdata_t " << this); } ~transaction_xdata_t() { DEBUG("ledger.memory.dtors", "dtor transaction_xdata_t " << this); if (component_xacts) delete component_xacts; } void remember_xact(transaction_t& xact) { if (! component_xacts) component_xacts = new transactions_list; component_xacts->push_back(&xact); } bool have_component_xacts() const { return component_xacts != NULL && ! component_xacts->empty(); } void copy_component_xacts(transactions_list& xacts) { for (transactions_list::const_iterator i = xacts.begin(); i != xacts.end(); i++) remember_xact(**i); } void walk_component_xacts(item_handler& handler) const { for (transactions_list::const_iterator i = component_xacts->begin(); i != component_xacts->end(); i++) handler(**i); } }; inline bool transaction_has_xdata(const transaction_t& xact) { return xact.data != NULL; } inline transaction_xdata_t& transaction_xdata_(const transaction_t& xact) { return *((transaction_xdata_t *) xact.data); } transaction_xdata_t& transaction_xdata(const transaction_t& xact); void add_transaction_to(const transaction_t& xact, value_t& value); inline account_t * xact_account(transaction_t& xact) { account_t * account = transaction_xdata(xact).account; if (account) return account; return xact.account; } inline const account_t * xact_account(const transaction_t& xact) { return xact_account(const_cast(xact)); } ////////////////////////////////////////////////////////////////////// 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_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 clear_transaction_xdata : public item_handler { public: virtual void operator()(transaction_t& xact) { if (xact.data) { delete (transaction_xdata_t *) xact.data; xact.data = NULL; } } }; class truncate_entries : public item_handler { int head_count; int tail_count; transactions_list xacts; public: truncate_entries(item_handler * handler, int _head_count, int _tail_count) : item_handler(handler), head_count(_head_count), tail_count(_tail_count) {} virtual void flush(); virtual void operator()(transaction_t& xact) { xacts.push_back(&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 push_to_transactions_list : public item_handler { public: transactions_list& xact_list; push_to_transactions_list(transactions_list& _xact_list) : xact_list(_xact_list) {} virtual void operator()(transaction_t& xact) { xact_list.push_back(&xact); } }; class sort_transactions : public item_handler { typedef std::deque transactions_deque; transactions_deque transactions; const value_expr sort_order; public: sort_transactions(item_handler * handler, const value_expr& _sort_order) : item_handler(handler), sort_order(_sort_order) {} sort_transactions(item_handler * handler, const string& _sort_order) : item_handler(handler), sort_order(_sort_order) {} virtual void post_accumulated_xacts(); virtual void flush() { post_accumulated_xacts(); item_handler::flush(); } virtual void operator()(transaction_t& xact) { transactions.push_back(&xact); } }; class sort_entries : public item_handler { sort_transactions sorter; entry_t * last_entry; public: sort_entries(item_handler * handler, const value_expr& _sort_order) : sorter(handler, _sort_order) {} sort_entries(item_handler * handler, const string& _sort_order) : sorter(handler, _sort_order) {} virtual void flush() { sorter.flush(); item_handler::flush(); } virtual void operator()(transaction_t& xact) { if (last_entry && xact.entry != last_entry) sorter.post_accumulated_xacts(); sorter(xact); last_entry = xact.entry; } }; class filter_transactions : public item_handler { item_predicate pred; public: filter_transactions(item_handler * handler, const value_expr& predicate) : item_handler(handler), pred(predicate) {} filter_transactions(item_handler * handler, const string& predicate) : item_handler(handler), pred(predicate) {} virtual void operator()(transaction_t& xact) { if (pred(xact)) { transaction_xdata(xact).dflags |= TRANSACTION_MATCHES; (*handler)(xact); } } }; class calc_transactions : public item_handler { transaction_t * last_xact; public: calc_transactions(item_handler * handler) : item_handler(handler), last_xact(NULL) {} virtual void operator()(transaction_t& xact); }; class invert_transactions : public item_handler { public: invert_transactions(item_handler * handler) : item_handler(handler) {} virtual void operator()(transaction_t& xact); }; inline void clear_entries_transactions(std::list& entries_list) { for (std::list::iterator i = entries_list.begin(); i != entries_list.end(); i++) (*i).transactions.clear(); } class collapse_transactions : public item_handler { value_t subtotal; unsigned int count; entry_t * last_entry; transaction_t * last_xact; account_t totals_account; std::list entry_temps; std::list xact_temps; public: collapse_transactions(item_handler * handler) : item_handler(handler), count(0), last_entry(NULL), last_xact(NULL), totals_account(NULL, "") {} ~collapse_transactions() { clear_entries_transactions(entry_temps); } virtual void flush() { if (subtotal) report_subtotal(); item_handler::flush(); } void report_subtotal(); virtual void operator()(transaction_t& xact); }; class component_transactions : public item_handler { item_predicate pred; public: component_transactions(item_handler * handler, const value_expr& predicate) : item_handler(handler), pred(predicate) {} component_transactions(item_handler * handler, const string& predicate) : item_handler(handler), pred(predicate) {} virtual void operator()(transaction_t& xact); }; class related_transactions : public item_handler { transactions_list transactions; bool also_matching; public: related_transactions(item_handler * handler, const bool _also_matching = false) : item_handler(handler), also_matching(_also_matching) {} virtual void flush(); virtual void operator()(transaction_t& xact) { transaction_xdata(xact).dflags |= TRANSACTION_RECEIVED; transactions.push_back(&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; value_t last_balance; 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) {} ~changed_value_transactions() { clear_entries_transactions(entry_temps); } virtual void flush() { if (last_xact) { output_diff(current_moment); last_xact = NULL; } item_handler::flush(); } void output_diff(const datetime_t& current); virtual void operator()(transaction_t& xact); }; class subtotal_transactions : public item_handler { struct acct_value_t { account_t * account; value_t value; transactions_list components; acct_value_t(account_t * a) : account(a) {} acct_value_t(account_t * a, value_t& v) : account(a), value(v) {} acct_value_t(const acct_value_t& av) : account(av.account), value(av.value) {} }; typedef std::map values_map; typedef std::pair values_pair; protected: values_map values; bool remember_components; std::list entry_temps; std::list xact_temps; public: datetime_t start; datetime_t finish; subtotal_transactions(item_handler * handler, bool _remember_components = false) : item_handler(handler), remember_components(_remember_components) {} #ifdef DEBUG_ENABLED subtotal_transactions(const subtotal_transactions&) { assert(false); } #endif virtual ~subtotal_transactions() { clear_entries_transactions(entry_temps); } void report_subtotal(const char * spec_fmt = NULL); virtual void flush() { if (values.size() > 0) report_subtotal(); item_handler::flush(); } virtual void operator()(transaction_t& xact); }; class interval_expr_error : public error { public: interval_expr_error(const string& reason, error_context * ctxt = NULL) throw() : error(reason, ctxt) {} virtual ~interval_expr_error() throw() {} }; class interval_transactions : public subtotal_transactions { interval_t interval; transaction_t * last_xact; bool started; public: interval_transactions(item_handler * _handler, const interval_t& _interval, bool remember_components = false) : subtotal_transactions(_handler, remember_components), interval(_interval), last_xact(NULL), started(false) {} interval_transactions(item_handler * _handler, const string& _interval, bool remember_components = false) : subtotal_transactions(_handler, remember_components), interval(_interval), last_xact(NULL), started(false) {} void report_subtotal(const datetime_t& moment = datetime_t()); virtual void flush() { if (last_xact) report_subtotal(); subtotal_transactions::flush(); } virtual void operator()(transaction_t& xact); }; class by_payee_transactions : public item_handler { typedef std::map payee_subtotals_map; typedef std::pair payee_subtotals_pair; payee_subtotals_map payee_subtotals; bool remember_components; public: by_payee_transactions(item_handler * handler, bool _remember_components = false) : item_handler(handler), remember_components(_remember_components) {} virtual ~by_payee_transactions(); virtual void flush(); virtual void operator()(transaction_t& xact); }; class set_comm_as_payee : public item_handler { std::list entry_temps; std::list xact_temps; public: set_comm_as_payee(item_handler * handler) : item_handler(handler) {} ~set_comm_as_payee() { clear_entries_transactions(entry_temps); } virtual void operator()(transaction_t& xact); }; class set_code_as_payee : public item_handler { std::list entry_temps; std::list xact_temps; public: set_code_as_payee(item_handler * handler) : item_handler(handler) {} ~set_code_as_payee() { clear_entries_transactions(entry_temps); } 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, bool remember_components = false) : subtotal_transactions(handler, remember_components) {} virtual void flush(); virtual void operator()(transaction_t& xact) { days_of_the_week[xact.date().date().day_of_week()].push_back(&xact); } }; class generate_transactions : public item_handler { protected: typedef std::pair pending_xacts_pair; typedef std::list pending_xacts_list; pending_xacts_list pending_xacts; std::list entry_temps; std::list xact_temps; public: generate_transactions(item_handler * handler) : item_handler(handler) {} ~generate_transactions() { clear_entries_transactions(entry_temps); } void add_period_entries(period_entries_list& period_entries); virtual void add_transaction(const interval_t& period, transaction_t& xact); }; #define BUDGET_NO_BUDGET 0x00 #define BUDGET_BUDGETED 0x01 #define BUDGET_UNBUDGETED 0x02 class budget_transactions : public generate_transactions { unsigned short flags; public: budget_transactions(item_handler * handler, unsigned long _flags = BUDGET_BUDGETED) : generate_transactions(handler), flags(_flags) {} void report_budget_items(const datetime_t& moment); virtual void operator()(transaction_t& xact); }; class forecast_transactions : public generate_transactions { item_predicate pred; public: forecast_transactions(item_handler * handler, const value_expr& predicate) : generate_transactions(handler), pred(predicate) {} forecast_transactions(item_handler * handler, const string& predicate) : generate_transactions(handler), pred(predicate) {} virtual void add_transaction(const interval_t& period, transaction_t& xact); virtual void flush(); }; ////////////////////////////////////////////////////////////////////// // // Account walking functions // #define ACCOUNT_TO_DISPLAY 0x0001 #define ACCOUNT_DISPLAYED 0x0002 #define ACCOUNT_SORT_CALC 0x0004 #define ACCOUNT_HAS_NON_VIRTUALS 0x0008 #define ACCOUNT_HAS_UNB_VIRTUALS 0x0010 struct account_xdata_t { value_t value; value_t total; value_t sort_value; unsigned int count; // transactions counted toward amount unsigned int total_count; // transactions counted toward total unsigned int virtuals; unsigned short dflags; account_xdata_t() : count(0), total_count(0), virtuals(0), dflags(0) {} }; inline bool account_has_xdata(const account_t& account) { return account.data != NULL; } inline account_xdata_t& account_xdata_(const account_t& account) { return *((account_xdata_t *) account.data); } account_xdata_t& account_xdata(const account_t& account); ////////////////////////////////////////////////////////////////////// class clear_account_xdata : public item_handler { public: virtual void operator()(account_t& acct) { if (acct.data) { delete (account_xdata_t *) acct.data; acct.data = NULL; } } }; void sum_accounts(account_t& account); typedef std::deque accounts_deque; void sort_accounts(account_t& account, const value_expr& sort_order, accounts_deque& accounts); void walk_accounts(account_t& account, item_handler& handler, const optional& sort_order = none); void walk_accounts(account_t& account, item_handler& handler, const string& sort_string); ////////////////////////////////////////////////////////////////////// void walk_commodities(commodity_pool_t::commodities_by_ident& commodities, item_handler& handler); inline void clear_journal_xdata(journal_t * journal) { clear_transaction_xdata xact_cleaner; walk_entries(journal->entries, xact_cleaner); clear_account_xdata acct_cleaner; walk_accounts(*journal->master, acct_cleaner); } } // namespace ledger #endif // _WALK_H