diff options
-rw-r--r-- | amount.cc | 12 | ||||
-rw-r--r-- | amount.h | 12 | ||||
-rw-r--r-- | binary.cc | 2 | ||||
-rw-r--r-- | derive.cc | 68 | ||||
-rw-r--r-- | derive.h | 4 | ||||
-rw-r--r-- | format.cc | 4 | ||||
-rw-r--r-- | format.h | 16 | ||||
-rw-r--r-- | journal.cc | 60 | ||||
-rw-r--r-- | journal.h | 38 | ||||
-rw-r--r-- | main.cc | 229 | ||||
-rw-r--r-- | parsexp.cc | 38 | ||||
-rw-r--r-- | reconcile.h | 4 | ||||
-rw-r--r-- | report.cc | 264 | ||||
-rw-r--r-- | report.h | 141 | ||||
-rw-r--r-- | session.cc | 38 | ||||
-rw-r--r-- | session.h | 31 | ||||
-rw-r--r-- | valexpr.h | 11 | ||||
-rw-r--r-- | walk.cc | 147 | ||||
-rw-r--r-- | walk.h | 376 |
19 files changed, 1054 insertions, 441 deletions
@@ -906,7 +906,7 @@ namespace { } } -void amount_t::parse(std::istream& in, flags_t flags) +bool amount_t::parse(std::istream& in, flags_t flags) { // The possible syntax for an amount is: // @@ -957,8 +957,12 @@ void amount_t::parse(std::istream& in, flags_t flags) } } - if (quant.empty()) - throw_(amount_error, "No quantity specified for amount"); + if (quant.empty()) { + if (flags & AMOUNT_PARSE_SOFT_FAIL) + return false; + else + throw_(amount_error, "No quantity specified for amount"); + } // Allocate memory for the amount's quantity value. We have to // monitor the allocation in an auto_ptr because this function gets @@ -1069,6 +1073,8 @@ void amount_t::parse(std::istream& in, flags_t flags) safe_holder.release(); // `this->quantity' owns the pointer assert(valid()); + + return true; } void amount_t::parse_conversion(const string& larger_str, @@ -590,16 +590,18 @@ public: * amount_t::parse_conversion("1.0m", "60s"); // a minute is 60 seconds * amount_t::parse_conversion("1.0h", "60m"); // an hour is 60 minutes */ -#define AMOUNT_PARSE_NO_MIGRATE 0x01 -#define AMOUNT_PARSE_NO_REDUCE 0x02 +#define AMOUNT_PARSE_NO_MIGRATE 0x01 +#define AMOUNT_PARSE_NO_REDUCE 0x02 +#define AMOUNT_PARSE_SOFT_FAIL 0x04 typedef uint_least8_t flags_t; - void parse(std::istream& in, flags_t flags = 0); - void parse(const string& str, flags_t flags = 0) { + bool parse(std::istream& in, flags_t flags = 0); + bool parse(const string& str, flags_t flags = 0) { std::istringstream stream(str); - parse(stream, flags); + bool result = parse(stream, flags); assert(stream.eof()); + return result; } static void parse_conversion(const string& larger_str, @@ -564,8 +564,6 @@ account_t * read_account(const char *& data, journal_t& journal, account_t * acct = new account_t(NULL); *accounts_next++ = acct; - acct->journal = &journal; - account_t::ident_t id; read_long(data, id); // parent id if (id == 0xffffffff) @@ -1,14 +1,15 @@ #include "derive.h" -#include "utils.h" -#include "mask.h" +#include "session.h" #include "walk.h" namespace ledger { -entry_t * derive_new_entry(journal_t& journal, +entry_t * derive_new_entry(report_t& report, strings_list::iterator i, strings_list::iterator end) { + session_t& session(report.session); + std::auto_ptr<entry_t> added(new entry_t); entry_t * matching = NULL; @@ -21,26 +22,32 @@ entry_t * derive_new_entry(journal_t& journal, mask_t regexp(*i++); + journals_iterator iter(session); entries_list::reverse_iterator j; - for (j = journal.entries.rbegin(); - j != journal.entries.rend(); - j++) - if (regexp.match((*j)->payee)) { - matching = *j; - break; + + for (journal_t * journal = iter(); journal; journal = iter()) { + for (j = journal->entries.rbegin(); + j != journal->entries.rend(); + j++) { + if (regexp.match((*j)->payee)) { + matching = *j; + break; + } } + if (matching) break; + } added->payee = matching ? matching->payee : regexp.expr.str(); if (! matching) { account_t * acct; if (i == end || ((*i)[0] == '-' || std::isdigit((*i)[0]))) { - acct = journal.find_account("Expenses"); + acct = session.find_account("Expenses"); } else if (i != end) { - acct = journal.find_account_re(*i); + acct = session.find_account_re(*i); if (! acct) - acct = journal.find_account(*i); + acct = session.find_account(*i); assert(acct); i++; } @@ -58,12 +65,7 @@ entry_t * derive_new_entry(journal_t& journal, // account to which only dollars are applied would imply that // dollars are wanted now too. - std::auto_ptr<item_handler<transaction_t> > formatter; - formatter.reset(new set_account_value); - walk_entries(journal.entries, *formatter.get()); - formatter->flush(); - - sum_accounts(*journal.master); + report.sum_all_accounts(); value_t total = account_xdata(*acct).total; if (total.is_type(value_t::AMOUNT)) @@ -75,16 +77,16 @@ entry_t * derive_new_entry(journal_t& journal, if (i != end) { if (! acct) - acct = journal.find_account_re(*i); + acct = session.find_account_re(*i); if (! acct) - acct = journal.find_account(*i); + acct = session.find_account(*i); } if (! acct) { - if (journal.basket) - acct = journal.basket; + if (matching && matching->journal->basket) + acct = matching->journal->basket; else - acct = journal.find_account("Equity"); + acct = session.find_account("Equity"); } added->add_transaction(new transaction_t(acct)); @@ -115,9 +117,9 @@ entry_t * derive_new_entry(journal_t& journal, added->add_transaction(xact); if (i != end) { - account_t * acct = journal.find_account_re(*i); + account_t * acct = session.find_account_re(*i); if (! acct) - acct = journal.find_account(*i); + acct = session.find_account(*i); assert(acct); added->transactions.back()->account = acct; } @@ -132,7 +134,7 @@ entry_t * derive_new_entry(journal_t& journal, mask_t acct_regex(re_pat); - for (; j != journal.entries.rend(); j++) + for (; j != matching->journal->entries.rend(); j++) if (regexp.match((*j)->payee)) { entry_t * entry = *j; for (transactions_list::const_iterator x = @@ -159,16 +161,16 @@ entry_t * derive_new_entry(journal_t& journal, strings_list::iterator x = i; if (i != end && ++x == end) { - draw_acct = journal.find_account_re(*i); + draw_acct = session.find_account_re(*i); if (! draw_acct) - draw_acct = journal.find_account(*i); + draw_acct = session.find_account(*i); i++; } if (! acct) - acct = journal.find_account_re(re_pat); + acct = session.find_account_re(re_pat); if (! acct) - acct = journal.find_account(re_pat); + acct = session.find_account(re_pat); xact = new transaction_t(acct, amount); if (! xact->amount.commodity()) { @@ -189,9 +191,11 @@ entry_t * derive_new_entry(journal_t& journal, added->add_transaction(new transaction_t(draw_acct)); } - if (! run_hooks(journal.entry_finalize_hooks, *added, false) || + if ((matching && + ! run_hooks(matching->journal->entry_finalize_hooks, *added, false)) || ! added->finalize() || - ! run_hooks(journal.entry_finalize_hooks, *added, true)) + (matching && + ! run_hooks(matching->journal->entry_finalize_hooks, *added, true))) throw new error("Failed to finalize derived entry (check commodities)"); return added.release(); @@ -1,11 +1,11 @@ #ifndef _DERIVE_H #define _DERIVE_H -#include "journal.h" +#include "report.h" namespace ledger { -entry_t * derive_new_entry(journal_t& journal, +entry_t * derive_new_entry(report_t& report, strings_list::iterator begin, strings_list::iterator end); @@ -850,6 +850,7 @@ void print_entry(std::ostream& out, const entry_base_t& entry_base, assert(false); } +#if 0 format_entries formatter(out, print_format); walk_transactions(const_cast<transactions_list&>(entry_base.transactions), formatter); @@ -858,6 +859,7 @@ void print_entry(std::ostream& out, const entry_base_t& entry_base, clear_transaction_xdata cleaner; walk_transactions(const_cast<transactions_list&>(entry_base.transactions), cleaner); +#endif } bool disp_subaccounts_p(const account_t& account, @@ -918,7 +920,7 @@ bool display_account(const account_t& account, return ! account_to_show && (! disp_pred || (*disp_pred)(account)); } -void format_account::operator()(account_t& account) +void format_accounts::operator()(account_t& account) { if (display_account(account, disp_pred)) { if (! account.parent) { @@ -173,7 +173,7 @@ inline bool disp_subaccounts_p(const account_t& account) { bool display_account(const account_t& account, const optional<item_predicate<account_t> >& disp_pred); -class format_account : public item_handler<account_t> +class format_accounts : public item_handler<account_t> { std::ostream& output_stream; @@ -182,15 +182,15 @@ class format_account : public item_handler<account_t> public: format_t format; - format_account(std::ostream& _output_stream, - const string& _format, - const string& display_predicate = NULL) + format_accounts(std::ostream& _output_stream, + const string& _format, + const string& display_predicate = NULL) : output_stream(_output_stream), disp_pred(display_predicate), format(_format) { - TRACE_CTOR(format_account, "std::ostream&, const string&, const string&"); + TRACE_CTOR(format_accounts, "std::ostream&, const string&, const string&"); } - ~format_account() throw() { - TRACE_DTOR(format_account); + ~format_accounts() throw() { + TRACE_DTOR(format_accounts); } virtual void flush() { @@ -211,7 +211,7 @@ class format_equity : public item_handler<account_t> mutable value_t total; public: - format_equity(std::ostream& _output_stream, + format_equity(std::ostream& _output_stream, const string& _format, const string& display_predicate); @@ -30,10 +30,8 @@ */ #include "journal.h" -#include "utils.h" -#include "valexpr.h" #include "format.h" -#include "mask.h" +#include "session.h" namespace ledger { @@ -520,8 +518,6 @@ account_t * account_t::find_account(const string& name, return NULL; account = new account_t(this, first); - account->journal = journal; - std::pair<accounts_map::iterator, bool> result = accounts.insert(accounts_map::value_type(first, account)); assert(result.second); @@ -535,26 +531,6 @@ account_t * account_t::find_account(const string& name, return account; } -static inline -account_t * find_account_re_(account_t * account, const mask_t& regexp) -{ - if (regexp.match(account->fullname())) - return account; - - for (accounts_map::iterator i = account->accounts.begin(); - i != account->accounts.end(); - i++) - if (account_t * a = find_account_re_((*i).second, regexp)) - return a; - - return NULL; -} - -account_t * journal_t::find_account_re(const string& regexp) -{ - return find_account_re_(master, mask_t(regexp)); -} - string account_t::fullname() const { if (! _fullname.empty()) { @@ -583,8 +559,8 @@ std::ostream& operator<<(std::ostream& out, const account_t& account) bool account_t::valid() const { - if (depth > 256 || ! journal) { - DEBUG("ledger.validate", "account_t: depth > 256 || ! journal"); + if (depth > 256) { + DEBUG("ledger.validate", "account_t: depth > 256"); return false; } @@ -605,13 +581,17 @@ bool account_t::valid() const return true; } +journal_t::journal_t(session_t * _owner) : + owner(_owner), basket(NULL), item_pool(NULL), item_pool_end(NULL) +{ + TRACE_CTOR(journal_t, ""); + master = owner->master; +} + journal_t::~journal_t() { TRACE_DTOR(journal_t); - assert(master); - checked_delete(master); - // Don't bother unhooking each entry's transactions from the // accounts they refer to, because all accounts are about to // be deleted. @@ -651,6 +631,26 @@ journal_t::~journal_t() checked_array_delete(item_pool); } +void journal_t::add_account(account_t * acct) +{ + owner->add_account(acct); +} + +bool journal_t::remove_account(account_t * acct) +{ + return owner->remove_account(acct); +} + +account_t * journal_t::find_account(const string& name, bool auto_create) +{ + return owner->find_account(name, auto_create); +} + +account_t * journal_t::find_account_re(const string& regexp) +{ + return owner->find_account_re(regexp); +} + bool journal_t::add_entry(entry_t * entry) { entry->journal = this; @@ -339,7 +339,6 @@ class account_t public: typedef unsigned long ident_t; - journal_t * journal; account_t * parent; string name; optional<string> note; @@ -358,8 +357,7 @@ class account_t TRACE_CTOR(account_t, "account_t *, const string&, const string&"); } account_t(const account_t& other) - : journal(other.journal), - parent(other.parent), + : parent(other.parent), name(other.name), note(other.note), depth(other.depth), @@ -379,11 +377,9 @@ class account_t void add_account(account_t * acct) { accounts.insert(accounts_map::value_type(acct->name, acct)); - acct->journal = journal; } bool remove_account(account_t * acct) { accounts_map::size_type n = accounts.erase(acct->name); - acct->journal = NULL; return n > 0; } @@ -468,37 +464,17 @@ class journal_t : public noncopyable auto_entries_list auto_entries; period_entries_list period_entries; - mutable accounts_map accounts_cache; std::list<entry_finalizer_t *> entry_finalize_hooks; - journal_t(session_t * _owner) : - owner(_owner), basket(NULL), item_pool(NULL), item_pool_end(NULL) { - TRACE_CTOR(journal_t, ""); - master = new account_t(NULL, ""); - master->journal = this; - } + journal_t(session_t * _owner); ~journal_t(); - void add_account(account_t * acct) { - master->add_account(acct); - acct->journal = this; - } - bool remove_account(account_t * acct) { - return master->remove_account(acct); - acct->journal = NULL; - } - - account_t * find_account(const string& name, bool auto_create = true) { - accounts_map::iterator c = accounts_cache.find(name); - if (c != accounts_cache.end()) - return (*c).second; - - account_t * account = master->find_account(name, auto_create); - accounts_cache.insert(accounts_map::value_type(name, account)); - account->journal = this; - return account; - } + // These four methods are delegated to 'owner', since all accounts processed + // are gathered together at the session level. + void add_account(account_t * acct); + bool remove_account(account_t * acct); + account_t * find_account(const string& name, bool auto_create = true); account_t * find_account_re(const string& regexp); bool add_entry(entry_t * entry); @@ -45,17 +45,6 @@ #include <fdstream.hpp> #endif -namespace ledger { - value_t register_command(expr::call_scope_t& args) - { - expr::var_t<std::ostream> ostream(args, "ostream"); - - *ostream << "This (will be) the register command.\n"; - - return true; - } -} - static int read_and_report(ledger::report_t& report, int argc, char * argv[], char * envp[]) { @@ -114,44 +103,58 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[], if (! session.use_cache) INFO("Binary cache mechanism will not be used"); - // Read the command word and create a command object based on it + // Configure the output stream - string verb = *arg++; +#ifdef HAVE_UNIX_PIPES + int status, pfd[2]; // Pipe file descriptors +#endif + std::ostream * out = &std::cout; - expr::function_t command; + if (report.output_file) { + out = new ofstream(*report.output_file); + } +#ifdef HAVE_UNIX_PIPES + else if (report.pager) { + status = pipe(pfd); + if (status == -1) + throw_(std::logic_error, "Failed to create pipe"); - if (verb == "register" || verb == "reg" || verb == "r") - command = register_command; -#if 0 - else if (verb == "balance" || verb == "bal" || verb == "b") - command = balance_command(); - else if (verb == "print" || verb == "p") - command = print_command(); - else if (verb == "equity") - command = equity_command(); - else if (verb == "entry") - command = entry_command(); - else if (verb == "dump") - command = dump_command(); - else if (verb == "output") - command = output_command(); - else if (verb == "prices") - command = prices_command(); - else if (verb == "pricesdb") - command = pricesdb_command(); - else if (verb == "csv") - command = csv_command(); - else if (verb == "emacs" || verb == "lisp") - command = emacs_command(); - else if (verb == "xml") - command = bind(xml_command, _1); - ; + status = fork(); + if (status < 0) { + throw_(std::logic_error, "Failed to fork child process"); + } + else if (status == 0) { // child + // Duplicate pipe's reading end into stdin + status = dup2(pfd[0], STDIN_FILENO); + if (status == -1) + perror("dup2"); + + // Close unuseful file descriptors: the pipe's writing and + // reading ends (the latter is not needed anymore, after the + // duplication). + close(pfd[1]); + close(pfd[0]); + + // Find command name: its the substring starting right of the + // rightmost '/' character in the pager pathname. See manpage + // for strrchr. + execlp(report.pager->native_file_string().c_str(), + basename(*report.pager).c_str(), (char *)0); + perror("execl"); + exit(1); + } + else { // parent + close(pfd[0]); + out = new boost::fdostream(pfd[1]); + } + } #endif - else if (verb == "expr") - ; - else if (verb == "xpath") - ; - else if (verb == "parse") { + + // Read the command word and create a command object based on it + + string verb = *arg++; + + if (verb == "parse") { value_expr expr(*arg); #if 0 @@ -184,17 +187,6 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[], return 0; } - else { - char buf[128]; - std::strcpy(buf, "command_"); - std::strcat(buf, verb.c_str()); - - if (expr::ptr_op_t def = report.lookup(buf)) - command = def->as_function(); - - if (! command) - throw_(std::logic_error, string("Unrecognized command '") + verb + "'"); - } // Parse the initialization file, which can only be textual; then // parse the journal data. @@ -221,55 +213,6 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[], TRACE_FINISH(entries, 1); TRACE_FINISH(parsing_total, 1); - // Configure the output stream - -#ifdef HAVE_UNIX_PIPES - int status, pfd[2]; // Pipe file descriptors -#endif - std::ostream * out = &std::cout; - - if (report.output_file) { - out = new ofstream(*report.output_file); - } -#ifdef HAVE_UNIX_PIPES - else if (report.pager) { - status = pipe(pfd); - if (status == -1) - throw_(std::logic_error, "Failed to create pipe"); - - status = fork(); - if (status < 0) { - throw_(std::logic_error, "Failed to fork child process"); - } - else if (status == 0) { // child - // Duplicate pipe's reading end into stdin - status = dup2(pfd[0], STDIN_FILENO); - if (status == -1) - perror("dup2"); - - // Close unuseful file descriptors: the pipe's writing and - // reading ends (the latter is not needed anymore, after the - // duplication). - close(pfd[1]); - close(pfd[0]); - - // Find command name: its the substring starting right of the - // rightmost '/' character in the pager pathname. See manpage - // for strrchr. - execlp(report.pager->native_file_string().c_str(), - basename(*report.pager).c_str(), (char *)0); - perror("execl"); - exit(1); - } - else { // parent - close(pfd[0]); - out = new boost::fdostream(pfd[1]); - } - } -#endif - - report.define("ostream", value_t(out)); - // Are we handling the expr commands? Do so now. if (verb == "expr") { @@ -294,11 +237,52 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[], return 0; } - // Apply transforms to the hierarchical document structure + // Read the command word and create a command object based on it - INFO_START(transforms, "Applied transforms"); - report.apply_transforms(*expr::global_scope); - INFO_FINISH(transforms); + if (verb == "register" || verb == "reg" || verb == "r") + report.transactions_report + (xact_handler_ptr(new format_transactions(*out, session.register_format))); + else if (verb == "balance" || verb == "bal" || verb == "b") + report.accounts_report + (acct_handler_ptr(new format_accounts(*out, session.balance_format, + report.display_predicate))); +#if 0 + else if (verb == "print" || verb == "p") + command = print_command(); + else if (verb == "equity") + command = equity_command(); + else if (verb == "entry") + command = entry_command(); + else if (verb == "dump") + command = dump_command(); + else if (verb == "output") + command = output_command(); + else if (verb == "prices") + command = prices_command(); + else if (verb == "pricesdb") + command = pricesdb_command(); + else if (verb == "csv") + command = csv_command(); + else if (verb == "emacs" || verb == "lisp") + command = emacs_command(); + else if (verb == "xml") + command = bind(xml_command, _1); + ; + else if (verb == "expr") + ; + else if (verb == "xpath") + ; + else { + char buf[128]; + std::strcpy(buf, "command_"); + std::strcat(buf, verb.c_str()); + + if (expr::ptr_op_t def = report.lookup(buf)) + command = def->as_function(); + + if (! command) + throw_(std::logic_error, string("Unrecognized command '") + verb + "'"); + } // Create an argument scope containing the report command's // arguments, and then invoke the command. @@ -313,31 +297,12 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[], command(command_args); INFO_FINISH(command); - - // Clean up memory, if we - - if (DO_VERIFY()) { - TRACE_START(cleanup, 1, "Cleaning up allocated memory"); - - clear_transaction_xdata xact_cleaner; - walk_entries(journal.entries, xact_cleaner); - - clear_account_xdata acct_cleaner; - walk_accounts(*journal.master, acct_cleaner); - - if (report.output_file) - checked_delete(out); - -#if 0 - for (std::list<item_handler<transaction_t> *>::iterator i - = formatter_ptrs.begin(); - i != formatter_ptrs.end(); - i++) - checked_delete(*i); #endif - TRACE_FINISH(cleanup, 1); - } + // Clean up memory, if it matters + + if (DO_VERIFY() && report.output_file) + checked_delete(out); // Write out the binary cache, if need be @@ -388,35 +388,27 @@ void parser_t::token_t::next(std::istream& in, const flags_t flags) // When in relaxed parsing mode, we want to migrate commodity // flags so that any precision specified by the user updates the // current maximum displayed precision. - try { - pos = (long)in.tellg(); - - unsigned char parse_flags = 0; - if (flags & EXPR_PARSE_NO_MIGRATE) - parse_flags |= AMOUNT_PARSE_NO_MIGRATE; - if (flags & EXPR_PARSE_NO_REDUCE) - parse_flags |= AMOUNT_PARSE_NO_REDUCE; + pos = (long)in.tellg(); - temp.parse(in, parse_flags); + unsigned char parse_flags = 0; + if (flags & EXPR_PARSE_NO_MIGRATE) + parse_flags |= AMOUNT_PARSE_NO_MIGRATE; + if (flags & EXPR_PARSE_NO_REDUCE) + parse_flags |= AMOUNT_PARSE_NO_REDUCE; - kind = VALUE; - value = temp; - } - catch (const amount_error& err) { + if (! temp.parse(in, parse_flags | AMOUNT_PARSE_SOFT_FAIL)) { // If the amount had no commodity, it must be an unambiguous // variable reference - // jww (2007-04-19): There must be a more efficient way to do this! - if (std::strcmp(err.what(), "No quantity specified for amount") == 0) { - in.clear(); - in.seekg(pos, std::ios::beg); + in.clear(); + in.seekg(pos, std::ios::beg); - c = in.peek(); - assert(! (std::isdigit(c) || c == '.')); - parse_ident(in); - } else { - throw; - } + c = in.peek(); + assert(! (std::isdigit(c) || c == '.')); + parse_ident(in); + } else { + kind = VALUE; + value = temp; } } break; diff --git a/reconcile.h b/reconcile.h index 31bec511..9f672d6b 100644 --- a/reconcile.h +++ b/reconcile.h @@ -16,13 +16,13 @@ class reconcile_transactions : public item_handler<transaction_t> reconcile_transactions(); public: - reconcile_transactions(item_handler<transaction_t> * handler, + reconcile_transactions(xact_handler_ptr handler, const value_t& _balance, const datetime_t& _cutoff) : item_handler<transaction_t>(handler), balance(_balance), cutoff(_cutoff) { TRACE_CTOR(reconcile_transactions, - "item_handler<transaction_t> *, const value_t&, const datetime_t&"); + "xact_handler_ptr, const value_t&, const datetime_t&"); } virtual ~reconcile_transactions() throw() { TRACE_DTOR(reconcile_transactions); @@ -30,20 +30,272 @@ */ #include "report.h" +#include "reconcile.h" namespace ledger { -void report_t::apply_transforms(expr::scope_t& scope) +xact_handler_ptr +report_t::chain_xact_handlers(xact_handler_ptr base_handler, + const bool handle_individual_transactions) { + bool remember_components = false; + + xact_handler_ptr handler(base_handler); + + // format_transactions write each transaction received to the + // output stream. + if (handle_individual_transactions) { + // truncate_entries cuts off a certain number of _entries_ from + // being displayed. It does not affect calculation. + if (head_entries || tail_entries) + handler.reset(new truncate_entries(handler, head_entries, tail_entries)); + + // filter_transactions will only pass through transactions + // matching the `display_predicate'. + if (! display_predicate.empty()) + handler.reset(new filter_transactions(handler, display_predicate)); + + // calc_transactions computes the running total. When this + // appears will determine, for example, whether filtered + // transactions are included or excluded from the running total. + handler.reset(new calc_transactions(handler)); + + // component_transactions looks for reported transaction that + // match the given `descend_expr', and then reports the + // transactions which made up the total for that reported + // transaction. + if (! descend_expr.empty()) { + std::list<std::string> descend_exprs; + + std::string::size_type beg = 0; + for (std::string::size_type pos = descend_expr.find(';'); + pos != std::string::npos; + beg = pos + 1, pos = descend_expr.find(';', beg)) + descend_exprs.push_back(std::string(descend_expr, beg, pos - beg)); + descend_exprs.push_back(std::string(descend_expr, beg)); + + for (std::list<std::string>::reverse_iterator i = + descend_exprs.rbegin(); + i != descend_exprs.rend(); + i++) + handler.reset(new component_transactions(handler, *i)); + + remember_components = true; + } + + // reconcile_transactions will pass through only those + // transactions which can be reconciled to a given balance + // (calculated against the transactions which it receives). + if (! reconcile_balance.empty()) { + datetime_t cutoff = current_moment; + if (! reconcile_date.empty()) + cutoff = parse_datetime(reconcile_date); + handler.reset(new reconcile_transactions + (handler, value_t(reconcile_balance), cutoff)); + } + + // filter_transactions will only pass through transactions + // matching the `secondary_predicate'. + if (! secondary_predicate.empty()) + handler.reset(new filter_transactions(handler, secondary_predicate)); + + // sort_transactions will sort all the transactions it sees, based + // on the `sort_order' value expression. + if (! sort_string.empty()) { + if (entry_sort) + handler.reset(new sort_entries(handler, sort_string)); + else + handler.reset(new sort_transactions(handler, sort_string)); + } + + // changed_value_transactions adds virtual transactions to the + // list to account for changes in market value of commodities, + // which otherwise would affect the running total unpredictably. + if (show_revalued) + handler.reset(new changed_value_transactions(handler, show_revalued_only)); + + // collapse_transactions causes entries with multiple transactions + // to appear as entries with a subtotaled transaction for each + // commodity used. + if (show_collapsed) + handler.reset(new collapse_transactions(handler)); + + // subtotal_transactions combines all the transactions it receives + // into one subtotal entry, which has one transaction for each + // commodity in each account. + // + // period_transactions is like subtotal_transactions, but it + // subtotals according to time periods rather than totalling + // everything. + // + // dow_transactions is like period_transactions, except that it + // reports all the transactions that fall on each subsequent day + // of the week. + if (show_subtotal) + handler.reset(new subtotal_transactions(handler, remember_components)); + + if (days_of_the_week) + handler.reset(new dow_transactions(handler, remember_components)); + else if (by_payee) + handler.reset(new by_payee_transactions(handler, remember_components)); + + // interval_transactions groups transactions together based on a + // time period, such as weekly or monthly. + if (! report_period.empty()) { + handler.reset(new interval_transactions(handler, report_period, + remember_components)); + handler.reset(new sort_transactions(handler, "d")); + } + } + + // invert_transactions inverts the value of the transactions it + // receives. + if (show_inverted) + handler.reset(new invert_transactions(handler)); + + // related_transactions will pass along all transactions related + // to the transaction received. If `show_all_related' is true, + // then all the entry's transactions are passed; meaning that if + // one transaction of an entry is to be printed, all the + // transaction for that entry will be printed. + if (show_related) + handler.reset(new related_transactions(handler, show_all_related)); + + // This filter_transactions will only pass through transactions + // matching the `predicate'. + if (! predicate.empty()) + handler.reset(new filter_transactions(handler, predicate)); + #if 0 - typedef tuple<shared_ptr<transform_t>, value_t> transform_details_tuple; + // budget_transactions takes a set of transactions from a data + // file and uses them to generate "budget transactions" which + // balance against the reported transactions. + // + // forecast_transactions is a lot like budget_transactions, except + // that it adds entries only for the future, and does not balance + // them against anything but the future balance. + + if (budget_flags) { + budget_transactions * budget_handler + = new budget_transactions(handler, budget_flags); + budget_handler->add_period_entries(journal->period_entries); + handler.reset(budget_handler; - foreach (transform_details_tuple& transform_details, transforms) { - expr::call_scope_t call_args(scope); - call_args.set_args(transform_details.get<1>()); - (*transform_details.get<0>())(call_args); + // Apply this before the budget handler, so that only matching + // transactions are calculated toward the budget. The use of + // filter_transactions above will further clean the results so + // that no automated transactions that don't match the filter get + // reported. + if (! predicate.empty()) + handler.reset(new filter_transactions(handler, predicate)); + } + else if (! forecast_limit.empty()) { + forecast_transactions * forecast_handler + = new forecast_transactions(handler, forecast_limit); + forecast_handler->add_period_entries(journal->period_entries); + handler.reset(forecast_handler; + + // See above, under budget_transactions. + if (! predicate.empty()) + handler.reset(new filter_transactions(handler, predicate)); } #endif + + if (comm_as_payee) + handler.reset(new set_comm_as_payee(handler)); + else if (code_as_payee) + handler.reset(new set_code_as_payee(handler)); + + return handler; +} + +void report_t::transactions_report(xact_handler_ptr handler) +{ + session_transactions_iterator walker(session); + pass_down_transactions(chain_xact_handlers(handler), walker); + handler->flush(); + + if (DO_VERIFY()) + clean_transactions(); +} + +void report_t::entry_report(xact_handler_ptr handler, entry_t& entry) +{ + entry_transactions_iterator walker(entry); + pass_down_transactions(chain_xact_handlers(handler), walker); + handler->flush(); + + if (DO_VERIFY()) + clean_transactions(entry); +} + +void report_t::sum_all_accounts() +{ + session_transactions_iterator walker(session); + pass_down_transactions + (chain_xact_handlers(xact_handler_ptr(new set_account_value), false), + walker); + // no flush() needed with set_account_value + sum_accounts(*session.master); +} + +void report_t::accounts_report(acct_handler_ptr handler, + const bool print_final_total) +{ + sum_all_accounts(); + + if (sort_string.empty()) { + accounts_iterator walker(*session.master); + pass_down_accounts<accounts_iterator>(handler, walker); + } else { + sorted_accounts_iterator walker(*session.master, sort_string); + pass_down_accounts<sorted_accounts_iterator>(handler, walker); + } + handler->flush(); + + if (print_final_total && account_has_xdata(*session.master)) { + account_xdata_t& xdata = account_xdata(*session.master); + if (! show_collapsed && xdata.total) { +#if 0 + *out << "--------------------\n"; + xdata.value = xdata.total; + handler->format.format(*out, details_t(*journal->master)); +#endif + } + } + + if (DO_VERIFY()) { + clean_transactions(); + clean_accounts(); + } +} + +void report_t::commodities_report(const string& format) +{ +} + +void report_t::entry_report(const entry_t& entry, const string& format) +{ +} + +void report_t::clean_transactions() +{ + session_transactions_iterator walker(session); + pass_down_transactions + (xact_handler_ptr(new clear_transaction_xdata), walker); +} + +void report_t::clean_transactions(entry_t& entry) +{ + entry_transactions_iterator walker(entry); + pass_down_transactions(xact_handler_ptr(new clear_transaction_xdata), walker); +} + +void report_t::clean_accounts() +{ + accounts_iterator acct_walker(*session.master); + pass_down_accounts<accounts_iterator> + (acct_handler_ptr(new clear_account_xdata), acct_walker); } value_t report_t::abbrev(expr::call_scope_t& args) @@ -33,9 +33,54 @@ #define _REPORT_H #include "session.h" +#include "walk.h" namespace ledger { +// These are the elements of any report: +// +// 1. Formatting string used for outputting the underlying ReportedType. +// +// 2. Handler object for the ReportedType. This is constructed using #1, or +// else #1 is ignored completely. This handler object is also constructed +// with the output stream that will be used during formatting. +// +// --- The details of #1 and #2 together represent the ItemHandler. +// +// 3. Mode of the report. Currently there are four modes: +// +// a. Transaction or commodity iteration. In this mode, all the journal's +// entries, the transactions of a specific entry, or all the journal's +// commodities are walked. In the first two cases, it's the underlying +// transactions which are passed to #2; in the second case, each +// commodity is passed to #2. +// +// b. Account iteration. This employs step 'a', but add a prologue and +// epilogue to it. In the prologue it "sums" all account totals and +// subtotals; in the epilogue it calls yet another handler whose job is +// reporting (the handler used in 'a' is only for calculation). +// +// There is one variation on 'b' in which a "totals" line is also +// displayed. +// +// c. Write journal. In this mode, a single function is called that output +// the journal object as a textual file. #2 is used to print out each +// transaction in the journal. +// +// d. Dump binary file. This is just like 'c', except that it dumps out a +// binary file and #2 is completely ignored. +// +// 4. For 'a' and 'b' in #3, there is a different iteration function called, +// depending on whether we're iterating: +// +// a. The transactions of an entry: walk_transactions. +// b. The entries of a journal: walk_entries. +// c. The commodities of a journal: walk_commodities. +// +// 5. Finally, for the 'a' and 'b' reporting modes, there is a variant which +// says that the formatter should be "flushed" after the entities are +// iterated. This does not happen for the commodities iteration, however. + class report_t : public expr::symbol_scope_t { report_t(); @@ -46,43 +91,115 @@ public: string amount_expr; string total_expr; string date_output_format; + string predicate; + string secondary_predicate; + string display_predicate; + string report_period; + string report_period_sort; + string sort_string; + string descend_expr; + string forecast_limit; + string reconcile_balance; + string reconcile_date; unsigned long budget_flags; + int head_entries; + int tail_entries; + + bool show_collapsed; + bool show_subtotal; + bool show_totals; + bool show_related; + bool show_all_related; + bool show_inverted; + bool show_empty; + bool days_of_the_week; + bool by_payee; + bool comm_as_payee; + bool code_as_payee; + bool show_revalued; + bool show_revalued_only; + bool keep_price; + bool keep_date; + bool keep_tag; + bool entry_sort; + bool sort_all; + string account; optional<path> pager; - bool show_totals; bool raw_mode; session_t& session; -#if 0 - transform_t * last_transform; - - std::list<tuple<shared_ptr<transform_t>, value_t> > transforms; -#endif explicit report_t(session_t& _session) : expr::symbol_scope_t(downcast<expr::scope_t>(_session)), + + head_entries(0), + tail_entries(0), + + show_collapsed(false), + show_subtotal(false), show_totals(false), + show_related(false), + show_all_related(false), + show_inverted(false), + show_empty(false), + days_of_the_week(false), + by_payee(false), + comm_as_payee(false), + code_as_payee(false), + show_revalued(false), + show_revalued_only(false), + keep_price(false), + keep_date(false), + keep_tag(false), + entry_sort(false), + sort_all(false), + raw_mode(false), + session(_session) -#if 0 - , - last_transform(NULL) -#endif { TRACE_CTOR(report_t, "session_t&"); + #if 0 eval("t=total,TOT=0,T()=(TOT=TOT+t,TOT)"); #endif + + value_expr::amount_expr.reset(new value_expr("@a")); + value_expr::total_expr.reset(new value_expr("@O")); } - virtual ~report_t() throw() { + virtual ~report_t() { TRACE_DTOR(report_t); } - void apply_transforms(expr::scope_t& scope); + // + // Actual report generation; this is why we're here... + // + + xact_handler_ptr + chain_xact_handlers(xact_handler_ptr handler, + const bool handle_individual_transactions = true); + + void transactions_report(xact_handler_ptr handler); + + void entry_report(xact_handler_ptr handler, entry_t& entry); + + void sum_all_accounts(); + + void accounts_report(acct_handler_ptr handler, + const bool print_final_total = true); + + void commodities_report(const string& format); + + void entry_report(const entry_t& entry, const string& format); + + void clean_transactions(); + void clean_transactions(entry_t& entry); + void clean_accounts(); // // Utility functions for value expressions @@ -73,22 +73,17 @@ session_t::session_t() : symbol_scope_t(), register_format - ("%((//entry)%{date} %-.20{payee}" - "%((./xact)%32|%-22{abbrev(account, 22)} %12.67t %12.80T\n))"), + ("%D %-.20P %-.22A %12.67t %!12.80T\n%/" + "%32|%-.22A %12.67t %!12.80T\n"), wide_register_format ("%D %-.35P %-.38A %22.108t %!22.132T\n%/" "%48|%-.38A %22.108t %!22.132T\n"), print_format -#if 1 - ("%(/%(/%{date} %-.20{payee}\n%(: %-34{account} %12t\n)\n))"), -#else ("\n%d %Y%C%P\n %-34W %12o%n\n%/ %-34W %12o%n\n"), -#endif balance_format - ("%(/%(//%20t %{\" \" * rdepth}%{rname}\n))--------------------\n%20t\n"), + ("%20T %2_%-a\n"), equity_format - - ("%((/)%{ftime(now, date_format)} %-.20{\"Opening Balance\"}\n%((.//account[value != 0]) %-34{fullname} %12{value}\n)\n)"), + ("\n%D %Y%C%P\n%/ %-34W %12t\n"), plot_amount_format ("%D %(@S(@t))\n"), plot_total_format @@ -116,7 +111,9 @@ session_t::session_t() abbrev_length(2), ansi_codes(false), - ansi_invert(false) + ansi_invert(false), + + master(new account_t(NULL, "")) { TRACE_CTOR(session_t, ""); } @@ -223,6 +220,27 @@ std::size_t session_t::read_data(journal_t& journal, return entry_count; } +namespace { + account_t * find_account_re_(account_t * account, const mask_t& regexp) + { + if (regexp.match(account->fullname())) + return account; + + for (accounts_map::iterator i = account->accounts.begin(); + i != account->accounts.end(); + i++) + if (account_t * a = find_account_re_((*i).second, regexp)) + return a; + + return NULL; + } +} + +account_t * session_t::find_account_re(const string& regexp) +{ + return find_account_re_(master, mask_t(regexp)); +} + #if 0 value_t session_t::resolve(const string& name, expr::scope_t& locals) { @@ -79,9 +79,16 @@ class session_t : public expr::symbol_scope_t ptr_list<journal_t> journals; ptr_list<parser_t> parsers; + account_t * master; + mutable accounts_map accounts_cache; + session_t(); - virtual ~session_t() throw() { + + virtual ~session_t() { TRACE_DTOR(session_t); + + assert(master); + checked_delete(master); } journal_t * create_journal() { @@ -130,6 +137,28 @@ class session_t : public expr::symbol_scope_t } // + // Dealing with accounts + // + + void add_account(account_t * acct) { + master->add_account(acct); + } + bool remove_account(account_t * acct) { + return master->remove_account(acct); + } + + account_t * find_account(const string& name, bool auto_create = true) { + accounts_map::iterator c = accounts_cache.find(name); + if (c != accounts_cache.end()) + return (*c).second; + + account_t * account = master->find_account(name, auto_create); + accounts_cache.insert(accounts_map::value_type(name, account)); + return account; + } + account_t * find_account_re(const string& regexp); + + // // Scope members // @@ -149,7 +149,7 @@ public: explicit scope_t(type_t _type) : type_(_type) { TRACE_CTOR(scope_t, "type_t"); } - virtual ~scope_t() throw() { + virtual ~scope_t() { TRACE_DTOR(scope_t); } @@ -193,7 +193,7 @@ public: : scope_t(_type), parent(&_parent) { TRACE_CTOR(child_scope_t, "scope_t&, type_t"); } - virtual ~child_scope_t() throw() { + virtual ~child_scope_t() { TRACE_DTOR(child_scope_t); } public: @@ -245,7 +245,7 @@ public: : child_scope_t(_parent, SYMBOL_SCOPE) { TRACE_CTOR(symbol_scope_t, "scope_t&"); } - virtual ~symbol_scope_t() throw() { + virtual ~symbol_scope_t() { TRACE_DTOR(symbol_scope_t); } @@ -267,7 +267,7 @@ public: : child_scope_t(_parent, CALL_SCOPE) { TRACE_CTOR(call_scope_t, "scope_t&"); } - virtual ~call_scope_t() throw() { + virtual ~call_scope_t() { TRACE_DTOR(call_scope_t); } @@ -862,6 +862,9 @@ public: reset(_expr.get()); return *this; } + value_expr& operator=(const string& _expr) { + return *this = value_expr(_expr); + } operator bool() const throw() { return ptr.get() != NULL; @@ -1,7 +1,7 @@ #include "walk.h" +#include "session.h" #include "format.h" #include "textual.h" -#include "util.h" #include <algorithm> @@ -49,7 +49,7 @@ void add_transaction_to(const transaction_t& xact, value_t& value) transaction_xdata_(xact).dflags & TRANSACTION_COMPOUND) { value += transaction_xdata_(xact).value; } - else if (xact.cost || ! value.is_realzero()) { + else if (xact.cost || (! value.is_null() && ! value.is_realzero())) { // jww (2008-04-24): Is this costly? value.add(xact.amount, xact.cost ? optional<amount_t>(*xact.cost) : none); } @@ -58,6 +58,57 @@ void add_transaction_to(const transaction_t& xact, value_t& value) } } +void entries_iterator::reset(session_t& session) +{ + journals_i = session.journals.begin(); + journals_end = session.journals.end(); + + journals_uninitialized = false; + + if (journals_i != journals_end) { + entries_i = (*journals_i).entries.begin(); + entries_end = (*journals_i).entries.end(); + + entries_uninitialized = false; + } else { + entries_uninitialized = true; + } +} + +entry_t * entries_iterator::operator()() +{ + if (entries_i == entries_end) { + journals_i++; + if (journals_i == journals_end) + return NULL; + + entries_i = (*journals_i).entries.begin(); + entries_end = (*journals_i).entries.end(); + } + return *entries_i++; +} + +void session_transactions_iterator::reset(session_t& session) +{ + entries.reset(session); + entry_t * entry = entries(); + if (entry != NULL) + xacts.reset(*entry); +} + +transaction_t * session_transactions_iterator::operator()() +{ + transaction_t * xact = xacts(); + if (xact == NULL) { + entry_t * entry = entries(); + if (entry != NULL) { + xacts.reset(*entry); + xact = xacts(); + } + } + return xact; +} + void truncate_entries::flush() { if (! xacts.size()) @@ -846,52 +897,59 @@ void sum_accounts(account_t& account) xdata.total_count += xdata.count; } -void sort_accounts(account_t& account, - const value_expr& sort_order, - accounts_deque& accounts) +account_t * accounts_iterator::operator()() { - for (accounts_map::iterator i = account.accounts.begin(); - i != account.accounts.end(); - i++) - accounts.push_back((*i).second); + while (! accounts_i.empty() && + accounts_i.back() == accounts_end.back()) { + accounts_i.pop_back(); + accounts_end.pop_back(); + } + if (accounts_i.empty()) + return NULL; + + account_t * account = (*(accounts_i.back()++)).second; + assert(account); - std::stable_sort(accounts.begin(), accounts.end(), - compare_items<account_t>(sort_order)); + // If this account has children, queue them up to be iterated next. + if (! account->accounts.empty()) + push_back(*account); + + return account; } -void walk_accounts(account_t& account, - item_handler<account_t>& handler, - const optional<value_expr>& sort_order) +void sorted_accounts_iterator::sort_accounts(account_t& account, + accounts_deque_t& deque) { - handler(account); + for (accounts_map::iterator i = account.accounts.begin(); + i != account.accounts.end(); + i++) + deque.push_back((*i).second); - if (sort_order) { - accounts_deque accounts; - sort_accounts(account, *sort_order, accounts); - for (accounts_deque::const_iterator i = accounts.begin(); - i != accounts.end(); - i++) { - account_xdata(**i).dflags &= ~ACCOUNT_SORT_CALC; - 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); - } + std::stable_sort(deque.begin(), deque.end(), + compare_items<account_t>(sort_cmp)); } -void walk_accounts(account_t& account, - item_handler<account_t>& handler, - const string& sort_string) +account_t * sorted_accounts_iterator::operator()() { - if (! sort_string.empty()) { - value_expr sorter(sort_string); - walk_accounts(account, handler, optional<value_expr>(sorter)); - } else { - walk_accounts(account, handler); + while (! sorted_accounts_i.empty() && + sorted_accounts_i.back() == sorted_accounts_end.back()) { + sorted_accounts_i.pop_back(); + sorted_accounts_end.pop_back(); + assert(! accounts_list.empty()); + accounts_list.pop_back(); } + if (sorted_accounts_i.empty()) + return NULL; + + account_t * account = *sorted_accounts_i.back()++; + assert(account); + + // If this account has children, queue them up to be iterated next. + if (! account->accounts.empty()) + push_back(*account); + + account_xdata(*account).dflags &= ~ACCOUNT_SORT_CALC; + return account; } void walk_commodities(commodity_pool_t::commodities_by_ident& commodities, @@ -933,4 +991,17 @@ void walk_commodities(commodity_pool_t::commodities_by_ident& commodities, clear_entries_transactions(entry_temps); } +void journals_iterator::reset(session_t& session) +{ + journals_i = session.journals.begin(); + journals_end = session.journals.end(); +} + +journal_t * journals_iterator::operator()() +{ + if (journals_i == journals_end) + return NULL; + return &(*journals_i++); +} + } // namespace ledger @@ -2,41 +2,37 @@ #define _WALK_H #include "journal.h" -#include "balance.h" -#include "valexpr.h" - -#include <iostream> -#include <fstream> -#include <deque> namespace ledger { template <typename T> struct item_handler : public noncopyable { - item_handler * handler; + shared_ptr<item_handler> handler; public: - item_handler() : handler(NULL) { + item_handler() { TRACE_CTOR(item_handler, ""); } - item_handler(item_handler * _handler) : handler(_handler) { - TRACE_CTOR(item_handler, "item_handler *"); + item_handler(shared_ptr<item_handler> _handler) : handler(_handler) { + TRACE_CTOR(item_handler, "shared_ptr<item_handler>"); } virtual ~item_handler() { TRACE_DTOR(item_handler); } virtual void flush() { - if (handler) + if (handler.get()) handler->flush(); } virtual void operator()(T& item) { - if (handler) - (*handler)(item); + if (handler.get()) + (*handler.get())(item); } }; +typedef shared_ptr<item_handler<transaction_t> > xact_handler_ptr; + template <typename T> class compare_items { @@ -167,29 +163,98 @@ inline const account_t * xact_account(const transaction_t& xact) { ////////////////////////////////////////////////////////////////////// -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); -} +class entries_iterator : public noncopyable +{ + ptr_list<journal_t>::iterator journals_i; + ptr_list<journal_t>::iterator journals_end; -inline void walk_transactions(transactions_list& list, - item_handler<transaction_t>& handler) { - walk_transactions(list.begin(), list.end(), handler); -} + bool journals_uninitialized; -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); -} + entries_list::iterator entries_i; + entries_list::iterator entries_end; -inline void walk_entries(entries_list& list, - item_handler<transaction_t>& handler) { - walk_entries(list.begin(), list.end(), handler); -} + bool entries_uninitialized; + +public: + entries_iterator() + : journals_uninitialized(true), entries_uninitialized(true) { + TRACE_CTOR(entries_iterator, ""); + } + entries_iterator(session_t& session) + : journals_uninitialized(true), entries_uninitialized(true) { + TRACE_CTOR(entries_iterator, "session_t&"); + reset(session); + } + ~entries_iterator() throw() { + TRACE_DTOR(entries_iterator); + } + + void reset(session_t& session); + + entry_t * operator()(); +}; + +class transactions_iterator : public noncopyable +{ +public: + virtual transaction_t * operator()() = 0; +}; + +class entry_transactions_iterator : public transactions_iterator +{ + transactions_list::iterator xacts_i; + transactions_list::iterator xacts_end; + + bool xacts_uninitialized; + +public: + entry_transactions_iterator() : xacts_uninitialized(true) { + TRACE_CTOR(entry_transactions_iterator, ""); + } + entry_transactions_iterator(entry_t& entry) + : xacts_uninitialized(true) { + TRACE_CTOR(entry_transactions_iterator, "entry_t&"); + reset(entry); + } + virtual ~entry_transactions_iterator() throw() { + TRACE_DTOR(entry_transactions_iterator); + } + + void reset(entry_t& entry) { + xacts_i = entry.transactions.begin(); + xacts_end = entry.transactions.end(); + + xacts_uninitialized = false; + } + + virtual transaction_t * operator()() { + if (xacts_i == xacts_end || xacts_uninitialized) + return NULL; + return *xacts_i++; + } +}; + +class session_transactions_iterator : public transactions_iterator +{ + entries_iterator entries; + entry_transactions_iterator xacts; + +public: + session_transactions_iterator() { + TRACE_CTOR(session_transactions_iterator, ""); + } + session_transactions_iterator(session_t& session) { + TRACE_CTOR(session_transactions_iterator, "session_t&"); + reset(session); + } + virtual ~session_transactions_iterator() throw() { + TRACE_DTOR(session_transactions_iterator); + } + + void reset(session_t& session); + + virtual transaction_t * operator()(); +}; ////////////////////////////////////////////////////////////////////// @@ -210,6 +275,25 @@ public: } }; +class pass_down_transactions : public item_handler<transaction_t> +{ + pass_down_transactions(); + +public: + pass_down_transactions(xact_handler_ptr handler, + transactions_iterator& iter) + : item_handler<transaction_t>(handler) { + TRACE_CTOR(pass_down_transactions, + "xact_handler_ptr, transactions_iterator"); + for (transaction_t * xact = iter(); xact; xact = iter()) + item_handler<transaction_t>::operator()(*xact); + } + + virtual ~pass_down_transactions() { + TRACE_DTOR(pass_down_transactions); + } +}; + class truncate_entries : public item_handler<transaction_t> { int head_count; @@ -220,11 +304,11 @@ class truncate_entries : public item_handler<transaction_t> truncate_entries(); public: - truncate_entries(item_handler<transaction_t> * handler, + truncate_entries(xact_handler_ptr handler, int _head_count, int _tail_count) : item_handler<transaction_t>(handler), head_count(_head_count), tail_count(_tail_count) { - TRACE_CTOR(truncate_entries, "item_handler<transaction_t> *, int, int"); + TRACE_CTOR(truncate_entries, "xact_handler_ptr, int, int"); } virtual ~truncate_entries() { TRACE_DTOR(truncate_entries); @@ -239,7 +323,7 @@ public: class set_account_value : public item_handler<transaction_t> { public: - set_account_value(item_handler<transaction_t> * handler = NULL) + set_account_value(xact_handler_ptr handler = xact_handler_ptr()) : item_handler<transaction_t>(handler) {} virtual void operator()(transaction_t& xact); @@ -275,19 +359,19 @@ class sort_transactions : public item_handler<transaction_t> sort_transactions(); public: - sort_transactions(item_handler<transaction_t> * handler, + sort_transactions(xact_handler_ptr handler, const value_expr& _sort_order) : item_handler<transaction_t>(handler), sort_order(_sort_order) { TRACE_CTOR(sort_transactions, - "item_handler<transaction_t> *, const value_expr&"); + "xact_handler_ptr, const value_expr&"); } - sort_transactions(item_handler<transaction_t> * handler, + sort_transactions(xact_handler_ptr handler, const string& _sort_order) : item_handler<transaction_t>(handler), sort_order(_sort_order) { TRACE_CTOR(sort_transactions, - "item_handler<transaction_t> *, const string&"); + "xact_handler_ptr, const string&"); } virtual ~sort_transactions() { TRACE_DTOR(sort_transactions); @@ -313,17 +397,17 @@ class sort_entries : public item_handler<transaction_t> sort_entries(); public: - sort_entries(item_handler<transaction_t> * handler, + sort_entries(xact_handler_ptr handler, const value_expr& _sort_order) : sorter(handler, _sort_order) { TRACE_CTOR(sort_entries, - "item_handler<transaction_t> *, const value_expr&"); + "xact_handler_ptr, const value_expr&"); } - sort_entries(item_handler<transaction_t> * handler, + sort_entries(xact_handler_ptr handler, const string& _sort_order) : sorter(handler, _sort_order) { TRACE_CTOR(sort_entries, - "item_handler<transaction_t> *, const string&"); + "xact_handler_ptr, const string&"); } virtual ~sort_entries() { TRACE_DTOR(sort_entries); @@ -351,18 +435,18 @@ class filter_transactions : public item_handler<transaction_t> filter_transactions(); public: - filter_transactions(item_handler<transaction_t> * handler, + filter_transactions(xact_handler_ptr handler, const value_expr& predicate) : item_handler<transaction_t>(handler), pred(predicate) { TRACE_CTOR(filter_transactions, - "item_handler<transaction_t> *, const value_expr&"); + "xact_handler_ptr, const value_expr&"); } - filter_transactions(item_handler<transaction_t> * handler, + filter_transactions(xact_handler_ptr handler, const string& predicate) : item_handler<transaction_t>(handler), pred(predicate) { TRACE_CTOR(filter_transactions, - "item_handler<transaction_t> *, const string&"); + "xact_handler_ptr, const string&"); } virtual ~filter_transactions() { TRACE_DTOR(filter_transactions); @@ -383,9 +467,9 @@ class calc_transactions : public item_handler<transaction_t> calc_transactions(); public: - calc_transactions(item_handler<transaction_t> * handler) + calc_transactions(xact_handler_ptr handler) : item_handler<transaction_t>(handler), last_xact(NULL) { - TRACE_CTOR(calc_transactions, "item_handler<transaction_t> *"); + TRACE_CTOR(calc_transactions, "xact_handler_ptr"); } virtual ~calc_transactions() { TRACE_DTOR(calc_transactions); @@ -399,7 +483,7 @@ class invert_transactions : public item_handler<transaction_t> invert_transactions(); public: - invert_transactions(item_handler<transaction_t> * handler) + invert_transactions(xact_handler_ptr handler) : item_handler<transaction_t>(handler) {} virtual void operator()(transaction_t& xact); @@ -426,11 +510,11 @@ class collapse_transactions : public item_handler<transaction_t> collapse_transactions(); public: - collapse_transactions(item_handler<transaction_t> * handler) + collapse_transactions(xact_handler_ptr handler) : item_handler<transaction_t>(handler), count(0), last_entry(NULL), last_xact(NULL), totals_account(NULL, "<Total>") { - TRACE_CTOR(collapse_transactions, "item_handler<transaction_t> *"); + TRACE_CTOR(collapse_transactions, "xact_handler_ptr"); } virtual ~collapse_transactions() { TRACE_DTOR(collapse_transactions); @@ -455,17 +539,17 @@ class component_transactions : public item_handler<transaction_t> component_transactions(); public: - component_transactions(item_handler<transaction_t> * handler, + component_transactions(xact_handler_ptr handler, const value_expr& predicate) : item_handler<transaction_t>(handler), pred(predicate) { TRACE_CTOR(component_transactions, - "item_handler<transaction_t> *, const value_expr&"); + "xact_handler_ptr, const value_expr&"); } - component_transactions(item_handler<transaction_t> * handler, + component_transactions(xact_handler_ptr handler, const string& predicate) : item_handler<transaction_t>(handler), pred(predicate) { TRACE_CTOR(component_transactions, - "item_handler<transaction_t> *, const string&"); + "xact_handler_ptr, const string&"); } virtual ~component_transactions() throw() { TRACE_DTOR(component_transactions); @@ -482,12 +566,12 @@ class related_transactions : public item_handler<transaction_t> related_transactions(); public: - related_transactions(item_handler<transaction_t> * handler, + related_transactions(xact_handler_ptr handler, const bool _also_matching = false) : item_handler<transaction_t>(handler), also_matching(_also_matching) { TRACE_CTOR(related_transactions, - "item_handler<transaction_t> *, const bool"); + "xact_handler_ptr, const bool"); } virtual ~related_transactions() throw() { TRACE_DTOR(related_transactions); @@ -515,12 +599,12 @@ class changed_value_transactions : public item_handler<transaction_t> changed_value_transactions(); public: - changed_value_transactions(item_handler<transaction_t> * handler, + changed_value_transactions(xact_handler_ptr handler, bool _changed_values_only) : item_handler<transaction_t>(handler), changed_values_only(_changed_values_only), last_xact(NULL) { TRACE_CTOR(changed_value_transactions, - "item_handler<transaction_t> *, bool"); + "xact_handler_ptr, bool"); } virtual ~changed_value_transactions() { TRACE_DTOR(changed_value_transactions); @@ -584,12 +668,12 @@ public: datetime_t start; datetime_t finish; - subtotal_transactions(item_handler<transaction_t> * handler, + subtotal_transactions(xact_handler_ptr handler, bool _remember_components = false) : item_handler<transaction_t>(handler), remember_components(_remember_components) { TRACE_CTOR(subtotal_transactions, - "item_handler<transaction_t> *, bool"); + "xact_handler_ptr, bool"); } virtual ~subtotal_transactions() { TRACE_DTOR(subtotal_transactions); @@ -623,21 +707,21 @@ class interval_transactions : public subtotal_transactions interval_transactions(); public: - interval_transactions(item_handler<transaction_t> * _handler, + interval_transactions(xact_handler_ptr _handler, const interval_t& _interval, bool remember_components = false) : subtotal_transactions(_handler, remember_components), interval(_interval), last_xact(NULL), started(false) { TRACE_CTOR(interval_transactions, - "item_handler<transaction_t> *, const interval_t&, bool"); + "xact_handler_ptr, const interval_t&, bool"); } - interval_transactions(item_handler<transaction_t> * _handler, + interval_transactions(xact_handler_ptr _handler, const string& _interval, bool remember_components = false) : subtotal_transactions(_handler, remember_components), interval(_interval), last_xact(NULL), started(false) { TRACE_CTOR(interval_transactions, - "item_handler<transaction_t> *, const string&, bool"); + "xact_handler_ptr, const string&, bool"); } virtual ~interval_transactions() throw() { TRACE_DTOR(interval_transactions); @@ -664,12 +748,12 @@ class by_payee_transactions : public item_handler<transaction_t> by_payee_transactions(); public: - by_payee_transactions(item_handler<transaction_t> * handler, + by_payee_transactions(xact_handler_ptr handler, bool _remember_components = false) : item_handler<transaction_t>(handler), remember_components(_remember_components) { TRACE_CTOR(by_payee_transactions, - "item_handler<transaction_t> *, bool"); + "xact_handler_ptr, bool"); } virtual ~by_payee_transactions(); @@ -685,9 +769,9 @@ class set_comm_as_payee : public item_handler<transaction_t> set_comm_as_payee(); public: - set_comm_as_payee(item_handler<transaction_t> * handler) + set_comm_as_payee(xact_handler_ptr handler) : item_handler<transaction_t>(handler) { - TRACE_CTOR(set_comm_as_payee, "item_handler<transaction_t> *"); + TRACE_CTOR(set_comm_as_payee, "xact_handler_ptr"); } virtual ~set_comm_as_payee() { TRACE_DTOR(set_comm_as_payee); @@ -705,9 +789,9 @@ class set_code_as_payee : public item_handler<transaction_t> set_code_as_payee(); public: - set_code_as_payee(item_handler<transaction_t> * handler) + set_code_as_payee(xact_handler_ptr handler) : item_handler<transaction_t>(handler) { - TRACE_CTOR(set_code_as_payee, "item_handler<transaction_t> *"); + TRACE_CTOR(set_code_as_payee, "xact_handler_ptr"); } virtual ~set_code_as_payee() { TRACE_DTOR(set_code_as_payee); @@ -724,10 +808,10 @@ class dow_transactions : public subtotal_transactions dow_transactions(); public: - dow_transactions(item_handler<transaction_t> * handler, + dow_transactions(xact_handler_ptr handler, bool remember_components = false) : subtotal_transactions(handler, remember_components) { - TRACE_CTOR(dow_transactions, "item_handler<transaction_t> *, bool"); + TRACE_CTOR(dow_transactions, "xact_handler_ptr, bool"); } virtual ~dow_transactions() throw() { TRACE_DTOR(dow_transactions); @@ -752,9 +836,9 @@ protected: std::list<transaction_t> xact_temps; public: - generate_transactions(item_handler<transaction_t> * handler) + generate_transactions(xact_handler_ptr handler) : item_handler<transaction_t>(handler) { - TRACE_CTOR(dow_transactions, "item_handler<transaction_t> *"); + TRACE_CTOR(dow_transactions, "xact_handler_ptr"); } virtual ~generate_transactions() { @@ -778,11 +862,11 @@ class budget_transactions : public generate_transactions budget_transactions(); public: - budget_transactions(item_handler<transaction_t> * handler, + budget_transactions(xact_handler_ptr handler, unsigned long _flags = BUDGET_BUDGETED) : generate_transactions(handler), flags(_flags) { TRACE_CTOR(budget_transactions, - "item_handler<transaction_t> *, unsigned long"); + "xact_handler_ptr, unsigned long"); } virtual ~budget_transactions() throw() { TRACE_DTOR(budget_transactions); @@ -798,17 +882,15 @@ class forecast_transactions : public generate_transactions item_predicate<transaction_t> pred; public: - forecast_transactions(item_handler<transaction_t> * handler, + forecast_transactions(xact_handler_ptr handler, const value_expr& predicate) : generate_transactions(handler), pred(predicate) { - TRACE_CTOR(forecast_transactions, - "item_handler<transaction_t> *, const value_expr&"); + TRACE_CTOR(forecast_transactions, "xact_handler_ptr, const value_expr&"); } - forecast_transactions(item_handler<transaction_t> * handler, + forecast_transactions(xact_handler_ptr handler, const string& predicate) : generate_transactions(handler), pred(predicate) { - TRACE_CTOR(forecast_transactions, - "item_handler<transaction_t> *, const string&"); + TRACE_CTOR(forecast_transactions, "xact_handler_ptr, const string&"); } virtual ~forecast_transactions() throw() { TRACE_DTOR(forecast_transactions); @@ -859,8 +941,76 @@ inline account_xdata_t& account_xdata_(const account_t& account) { account_xdata_t& account_xdata(const account_t& account); +void sum_accounts(account_t& account); + ////////////////////////////////////////////////////////////////////// +class accounts_iterator : public noncopyable +{ + std::list<accounts_map::const_iterator> accounts_i; + std::list<accounts_map::const_iterator> accounts_end; + +public: + accounts_iterator() { + TRACE_CTOR(accounts_iterator, ""); + } + accounts_iterator(account_t& account) { + TRACE_CTOR(accounts_iterator, "account_t&"); + push_back(account); + } + virtual ~accounts_iterator() throw() { + TRACE_DTOR(accounts_iterator); + } + + void push_back(account_t& account) { + accounts_i.push_back(account.accounts.begin()); + accounts_end.push_back(account.accounts.end()); + } + + virtual account_t * operator()(); +}; + +class sorted_accounts_iterator : public noncopyable +{ + value_expr sort_cmp; + + typedef std::deque<account_t *> accounts_deque_t; + + std::list<accounts_deque_t> accounts_list; + std::list<accounts_deque_t::const_iterator> sorted_accounts_i; + std::list<accounts_deque_t::const_iterator> sorted_accounts_end; + +public: + sorted_accounts_iterator(const string& sort_order) { + TRACE_CTOR(sorted_accounts_iterator, "const string&"); + sort_cmp = value_expr(sort_order); + } + sorted_accounts_iterator(account_t& account, const string& sort_order) { + TRACE_CTOR(sorted_accounts_iterator, "account_t&, const string&"); + sort_cmp = value_expr(sort_order); + push_back(account); + } + virtual ~sorted_accounts_iterator() throw() { + TRACE_DTOR(sorted_accounts_iterator); + } + + void sort_accounts(account_t& account, accounts_deque_t& deque); + + void push_back(account_t& account) { + accounts_list.push_back(accounts_deque_t()); + sort_accounts(account, accounts_list.back()); + + sorted_accounts_i.push_back(accounts_list.back().begin()); + sorted_accounts_end.push_back(accounts_list.back().end()); + } + + virtual account_t * operator()(); +}; + +////////////////////////////////////////////////////////////////////// + +typedef shared_ptr<item_handler<account_t> > acct_handler_ptr; + class clear_account_xdata : public item_handler<account_t> { public: @@ -872,25 +1022,28 @@ public: } }; -void sum_accounts(account_t& account); +template <typename Iterator> +class pass_down_accounts : public item_handler<account_t> +{ + pass_down_accounts(); -typedef std::deque<account_t *> accounts_deque; +public: + pass_down_accounts(acct_handler_ptr handler, Iterator& iter) + : item_handler<account_t>(handler) { + TRACE_CTOR(pass_down_accounts, + "acct_handler_ptr, accounts_iterator"); + for (account_t * account = iter(); account; account = iter()) + item_handler<account_t>::operator()(*account); + } -void sort_accounts(account_t& account, - const value_expr& sort_order, - accounts_deque& accounts); -void walk_accounts(account_t& account, - item_handler<account_t>& handler, - const optional<value_expr>& sort_order = none); -void walk_accounts(account_t& account, - item_handler<account_t>& handler, - const string& sort_string); + virtual ~pass_down_accounts() { + TRACE_DTOR(pass_down_accounts); + } +}; ////////////////////////////////////////////////////////////////////// -void walk_commodities(commodity_pool_t::commodities_by_ident& commodities, - item_handler<transaction_t>& handler); - +#if 0 inline void clear_journal_xdata(journal_t& journal) { clear_transaction_xdata xact_cleaner; walk_entries(journal.entries, xact_cleaner); @@ -898,6 +1051,31 @@ inline void clear_journal_xdata(journal_t& journal) { clear_account_xdata acct_cleaner; walk_accounts(*journal.master, acct_cleaner); } +#endif + +////////////////////////////////////////////////////////////////////// + +class journals_iterator : public noncopyable +{ + ptr_list<journal_t>::iterator journals_i; + ptr_list<journal_t>::iterator journals_end; + +public: + journals_iterator() { + TRACE_CTOR(journals_iterator, ""); + } + journals_iterator(session_t& session) { + TRACE_CTOR(journals_iterator, "session_t&"); + reset(session); + } + virtual ~journals_iterator() throw() { + TRACE_DTOR(journals_iterator); + } + + void reset(session_t& session); + + virtual journal_t * operator()(); +}; } // namespace ledger |