summaryrefslogtreecommitdiff
path: root/walk.h
diff options
context:
space:
mode:
Diffstat (limited to 'walk.h')
-rw-r--r--walk.h580
1 files changed, 580 insertions, 0 deletions
diff --git a/walk.h b/walk.h
new file mode 100644
index 00000000..a1dd14a2
--- /dev/null
+++ b/walk.h
@@ -0,0 +1,580 @@
+#ifndef _WALK_H
+#define _WALK_H
+
+#include "journal.h"
+#include "balance.h"
+#include "valexpr.h"
+#include "datetime.h"
+
+#include <iostream>
+#include <fstream>
+#include <deque>
+
+namespace ledger {
+
+template <typename T>
+struct item_handler {
+ item_handler * handler;
+
+ public:
+ item_handler() : handler(NULL) {
+ DEBUG_PRINT("ledger.memory.ctors", "ctor item_handler<T>");
+ }
+ item_handler(item_handler * _handler) : handler(_handler) {
+ DEBUG_PRINT("ledger.memory.ctors", "ctor item_handler<T>");
+ }
+
+ virtual ~item_handler() {
+ DEBUG_PRINT("ledger.memory.dtors", "dtor item_handler<T>");
+ }
+ virtual void flush() {
+ if (handler)
+ handler->flush();
+ }
+ virtual void operator()(T& item) {
+ if (handler)
+ (*handler)(item);
+ }
+};
+
+template <typename T>
+class compare_items {
+ 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);
+};
+
+template <typename T>
+bool compare_items<T>::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<transaction_t>::operator()(const transaction_t * left,
+ const transaction_t * right);
+template <>
+bool compare_items<account_t>::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_COMPOSITE 0x0040
+#define TRANSACTION_MATCHES 0x0080
+
+struct transaction_xdata_t
+{
+ value_t total;
+ value_t sort_value;
+ value_t composite_amount;
+ unsigned int index;
+ unsigned short dflags;
+ std::time_t date;
+
+ transaction_xdata_t() : index(0), dflags(0), date(0) {}
+};
+
+inline bool transaction_has_xdata(const transaction_t& xact) {
+ return xact.data != NULL;
+}
+
+extern std::list<transaction_xdata_t> transactions_xdata;
+extern std::list<void **> transactions_xdata_ptrs;
+
+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 void walk_transactions(transactions_list::iterator begin,
+ transactions_list::iterator end,
+ item_handler<transaction_t>& handler) {
+ for (transactions_list::iterator i = begin; i != end; i++)
+ handler(**i);
+}
+
+inline void walk_transactions(transactions_list& list,
+ item_handler<transaction_t>& handler) {
+ walk_transactions(list.begin(), list.end(), handler);
+}
+
+inline void walk_entries(entries_list::iterator begin,
+ entries_list::iterator end,
+ item_handler<transaction_t>& handler) {
+ for (entries_list::iterator i = begin; i != end; i++)
+ walk_transactions((*i)->transactions, handler);
+}
+
+inline void walk_entries(entries_list& list,
+ item_handler<transaction_t>& handler) {
+ walk_entries(list.begin(), list.end(), handler);
+}
+
+void clear_transactions_xdata();
+
+//////////////////////////////////////////////////////////////////////
+
+class ignore_transactions : public item_handler<transaction_t>
+{
+ public:
+ virtual void operator()(transaction_t& xact) {}
+};
+
+class set_account_value : public item_handler<transaction_t>
+{
+ public:
+ set_account_value(item_handler<transaction_t> * handler = NULL)
+ : item_handler<transaction_t>(handler) {}
+
+ virtual void operator()(transaction_t& xact);
+};
+
+class sort_transactions : public item_handler<transaction_t>
+{
+ typedef std::deque<transaction_t *> transactions_deque;
+
+ transactions_deque transactions;
+ const value_expr_t * sort_order;
+ bool allocated;
+
+ public:
+ sort_transactions(item_handler<transaction_t> * handler,
+ const value_expr_t * _sort_order)
+ : item_handler<transaction_t>(handler),
+ sort_order(_sort_order), allocated(false) {}
+
+ sort_transactions(item_handler<transaction_t> * handler,
+ const std::string& _sort_order)
+ : item_handler<transaction_t>(handler), allocated(false) {
+ try {
+ sort_order = parse_value_expr(_sort_order);
+ allocated = true;
+ }
+ catch (value_expr_error& err) {
+ throw value_expr_error(std::string("In sort string '") + _sort_order +
+ "': " + err.what());
+ }
+ }
+
+ 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<transaction_t>
+{
+ item_predicate<transaction_t> pred;
+
+ public:
+ filter_transactions(item_handler<transaction_t> * handler,
+ const value_expr_t * predicate)
+ : item_handler<transaction_t>(handler), pred(predicate) {}
+
+ filter_transactions(item_handler<transaction_t> * handler,
+ const std::string& predicate)
+ : item_handler<transaction_t>(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>
+{
+ transaction_t * last_xact;
+
+ public:
+ calc_transactions(item_handler<transaction_t> * handler)
+ : item_handler<transaction_t>(handler), last_xact(NULL) {}
+
+ virtual void operator()(transaction_t& xact);
+};
+
+class invert_transactions : public item_handler<transaction_t>
+{
+ public:
+ invert_transactions(item_handler<transaction_t> * handler)
+ : item_handler<transaction_t>(handler) {}
+
+ virtual void operator()(transaction_t& xact);
+};
+
+inline void clear_entries_transactions(std::list<entry_t>& entries_list) {
+ for (std::list<entry_t>::iterator i = entries_list.begin();
+ i != entries_list.end();
+ i++)
+ (*i).transactions.clear();
+}
+
+class collapse_transactions : public item_handler<transaction_t>
+{
+ value_t subtotal;
+ unsigned int count;
+ entry_t * last_entry;
+ transaction_t * last_xact;
+ account_t totals_account;
+
+ std::list<entry_t> entry_temps;
+ std::list<transaction_t> xact_temps;
+
+ public:
+ collapse_transactions(item_handler<transaction_t> * handler)
+ : item_handler<transaction_t>(handler), count(0),
+ last_entry(NULL), last_xact(NULL),
+ totals_account(NULL, "<Total>") {}
+
+ ~collapse_transactions() {
+ clear_entries_transactions(entry_temps);
+ }
+
+ virtual void flush() {
+ if (subtotal)
+ report_subtotal();
+ item_handler<transaction_t>::flush();
+ }
+
+ void report_subtotal();
+
+ virtual void operator()(transaction_t& xact);
+};
+
+class related_transactions : public item_handler<transaction_t>
+{
+ transactions_list transactions;
+ bool also_matching;
+
+ public:
+ related_transactions(item_handler<transaction_t> * handler,
+ const bool _also_matching = false)
+ : item_handler<transaction_t>(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<transaction_t>
+{
+ // 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_t> entry_temps;
+ std::list<transaction_t> xact_temps;
+
+ public:
+ changed_value_transactions(item_handler<transaction_t> * handler,
+ bool _changed_values_only)
+ : item_handler<transaction_t>(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(now);
+ last_xact = NULL;
+ }
+ item_handler<transaction_t>::flush();
+ }
+
+ void output_diff(const std::time_t current);
+
+ virtual void operator()(transaction_t& xact);
+};
+
+class subtotal_transactions : public item_handler<transaction_t>
+{
+ typedef std::map<account_t *, value_t> values_map;
+ typedef std::pair<account_t *, value_t> values_pair;
+
+ protected:
+ values_map values;
+
+ std::list<entry_t> entry_temps;
+ std::list<transaction_t> xact_temps;
+
+ public:
+ std::time_t start;
+ std::time_t finish;
+
+ subtotal_transactions(item_handler<transaction_t> * handler)
+ : item_handler<transaction_t>(handler), start(0), finish(0) {}
+#ifdef DEBUG_ENABLED
+ subtotal_transactions(const subtotal_transactions&) {
+ assert(0);
+ }
+#endif
+
+ ~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<transaction_t>::flush();
+ }
+ virtual void operator()(transaction_t& xact);
+};
+
+class interval_transactions : public subtotal_transactions
+{
+ interval_t interval;
+ transaction_t * last_xact;
+ bool started;
+
+ item_handler<transaction_t> * sorter;
+
+ public:
+ interval_transactions(item_handler<transaction_t> * _handler,
+ const interval_t& _interval,
+ const value_expr_t * sort_order = NULL)
+ : subtotal_transactions(_handler), interval(_interval),
+ last_xact(NULL), started(false), sorter(NULL) {
+ if (sort_order) {
+ sorter = new sort_transactions(handler, sort_order);
+ handler = sorter;
+ }
+ }
+ interval_transactions(item_handler<transaction_t> * _handler,
+ const std::string& _interval,
+ const std::string& sort_order = "")
+ : subtotal_transactions(_handler), interval(_interval),
+ last_xact(NULL), started(false), sorter(NULL) {
+ if (! sort_order.empty()) {
+ sorter = new sort_transactions(handler, sort_order);
+ handler = sorter;
+ }
+ }
+
+ virtual ~interval_transactions() {
+ if (sorter)
+ delete sorter;
+ }
+
+ void report_subtotal(const std::time_t moment = 0);
+
+ virtual void flush() {
+ if (last_xact)
+ report_subtotal();
+ subtotal_transactions::flush();
+ }
+ virtual void operator()(transaction_t& xact);
+};
+
+class by_payee_transactions : public item_handler<transaction_t>
+{
+ typedef std::map<std::string, subtotal_transactions *> payee_subtotals_map;
+ typedef std::pair<std::string, subtotal_transactions *> payee_subtotals_pair;
+
+ payee_subtotals_map payee_subtotals;
+
+ public:
+ by_payee_transactions(item_handler<transaction_t> * handler)
+ : item_handler<transaction_t>(handler) {}
+
+ virtual ~by_payee_transactions();
+
+ virtual void flush();
+ virtual void operator()(transaction_t& xact);
+};
+
+class set_comm_as_payee : public item_handler<transaction_t>
+{
+ std::list<entry_t> entry_temps;
+ std::list<transaction_t> xact_temps;
+
+ public:
+ set_comm_as_payee(item_handler<transaction_t> * handler)
+ : item_handler<transaction_t>(handler) {}
+
+ ~set_comm_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<transaction_t> * 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 generate_transactions : public item_handler<transaction_t>
+{
+ protected:
+ typedef std::pair<interval_t, transaction_t *> pending_xacts_pair;
+ typedef std::list<pending_xacts_pair> pending_xacts_list;
+
+ pending_xacts_list pending_xacts;
+ std::list<entry_t> entry_temps;
+ std::list<transaction_t> xact_temps;
+
+ public:
+ generate_transactions(item_handler<transaction_t> * handler)
+ : item_handler<transaction_t>(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<transaction_t> * handler,
+ unsigned long _flags = BUDGET_BUDGETED)
+ : generate_transactions(handler), flags(_flags) {}
+
+ void report_budget_items(const std::time_t moment);
+
+ virtual void operator()(transaction_t& xact);
+};
+
+class forecast_transactions : public generate_transactions
+{
+ item_predicate<transaction_t> pred;
+
+ public:
+ forecast_transactions(item_handler<transaction_t> * handler,
+ const value_expr_t * predicate)
+ : generate_transactions(handler), pred(predicate) {}
+
+ forecast_transactions(item_handler<transaction_t> * handler,
+ const std::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;
+}
+
+extern std::list<account_xdata_t> accounts_xdata;
+extern std::list<void **> accounts_xdata_ptrs;
+
+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);
+
+//////////////////////////////////////////////////////////////////////
+
+void sum_accounts(account_t& account);
+
+typedef std::deque<account_t *> accounts_deque;
+
+void sort_accounts(account_t& account,
+ const value_expr_t * sort_order,
+ accounts_deque& accounts);
+void walk_accounts(account_t& account,
+ item_handler<account_t>& handler,
+ const value_expr_t * sort_order = NULL);
+void walk_accounts(account_t& account,
+ item_handler<account_t>& handler,
+ const std::string& sort_string);
+void clear_accounts_xdata();
+
+//////////////////////////////////////////////////////////////////////
+
+void walk_commodities(commodities_map& commodities,
+ item_handler<transaction_t>& handler);
+
+} // namespace ledger
+
+#endif // _WALK_H