diff options
author | John Wiegley <johnw@newartisans.com> | 2008-08-01 01:37:20 -0400 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2008-08-01 01:37:58 -0400 |
commit | 567902b1731db6f1ace85a7bc384b55ffd0948ab (patch) | |
tree | 6073b110e309781eea7e05163b57ff90bec88d5f | |
parent | e5048ec71bf114c351c62844b7603893195df4d4 (diff) | |
download | fork-ledger-567902b1731db6f1ace85a7bc384b55ffd0948ab.tar.gz fork-ledger-567902b1731db6f1ace85a7bc384b55ffd0948ab.tar.bz2 fork-ledger-567902b1731db6f1ace85a7bc384b55ffd0948ab.zip |
Simplified the format_t code so that it no longer references any particulars
about journal objects. This is all done through value expressions now.
-rw-r--r-- | format.cc | 526 | ||||
-rw-r--r-- | format.h | 234 | ||||
-rw-r--r-- | gnucash.cc | 2 | ||||
-rw-r--r-- | report.cc | 259 | ||||
-rw-r--r-- | report.h | 117 | ||||
-rw-r--r-- | xml.h | 2 |
6 files changed, 545 insertions, 595 deletions
@@ -6,119 +6,36 @@ namespace ledger { -format_t::elision_style_t format_t::elision_style = ABBREVIATE; -int format_t::abbrev_length = 2; +format_t::elision_style_t + format_t::elision_style = ABBREVIATE; +int format_t::abbrev_length = 2; -bool format_t::ansi_codes = false; -bool format_t::ansi_invert = false; +bool format_t::ansi_codes = false; +bool format_t::ansi_invert = false; -string format_t::truncate(const string& str, unsigned int width, - const bool is_account) -{ - const unsigned int len = str.length(); - if (len <= width) - return str; - - assert(width < 4095); - - char buf[4096]; - - switch (elision_style) { - case TRUNCATE_LEADING: - // This method truncates at the beginning. - std::strncpy(buf, str.c_str() + (len - width), width); - buf[0] = '.'; - buf[1] = '.'; - break; - - case TRUNCATE_MIDDLE: - // This method truncates in the middle. - std::strncpy(buf, str.c_str(), width / 2); - std::strncpy(buf + width / 2, - str.c_str() + (len - (width / 2 + width % 2)), - width / 2 + width % 2); - buf[width / 2 - 1] = '.'; - buf[width / 2] = '.'; - break; - - case ABBREVIATE: - if (is_account) { - std::list<string> parts; - string::size_type beg = 0; - for (string::size_type pos = str.find(':'); - pos != string::npos; - beg = pos + 1, pos = str.find(':', beg)) - parts.push_back(string(str, beg, pos - beg)); - parts.push_back(string(str, beg)); - - string result; - unsigned int newlen = len; - for (std::list<string>::iterator i = parts.begin(); - i != parts.end(); - i++) { - // Don't contract the last element - std::list<string>::iterator x = i; - if (++x == parts.end()) { - result += *i; - break; - } +namespace { + string partial_account_name(const account_t& account) + { + string name; - if (newlen > width) { - result += string(*i, 0, abbrev_length); - result += ":"; - newlen -= (*i).length() - abbrev_length; - } else { - result += *i; - result += ":"; - } - } + for (const account_t * acct = &account; + acct && acct->parent; + acct = acct->parent) { + if (account_has_xdata(*acct) && + account_xdata_(*acct).dflags & ACCOUNT_DISPLAYED) + break; - if (newlen > width) { - // Even abbreviated its too big to show the last account, so - // abbreviate all but the last and truncate at the beginning. - std::strncpy(buf, result.c_str() + (result.length() - width), width); - buf[0] = '.'; - buf[1] = '.'; - } else { - std::strcpy(buf, result.c_str()); - } - break; + if (name.empty()) + name = acct->name; + else + name = acct->name + ":" + name; } - // fall through... - case TRUNCATE_TRAILING: - // This method truncates at the end (the default). - std::strncpy(buf, str.c_str(), width - 2); - buf[width - 2] = '.'; - buf[width - 1] = '.'; - break; + return name; } - buf[width] = '\0'; - - return buf; } -string partial_account_name(const account_t& account) -{ - string name; - - for (const account_t * acct = &account; - acct && acct->parent; - acct = acct->parent) { - if (account_has_xdata(*acct) && - account_xdata_(*acct).dflags & ACCOUNT_DISPLAYED) - break; - - if (name.empty()) - name = acct->name; - else - name = acct->name + ":" + name; - } - - return name; -} - -element_t * format_t::parse_elements(const string& fmt) +format_t::element_t * format_t::parse_elements(const string& fmt) { std::auto_ptr<element_t> result; @@ -137,8 +54,8 @@ element_t * format_t::parse_elements(const string& fmt) result.reset(new element_t); current = result.get(); } else { - current->next = new element_t; - current = current->next; + current->next.reset(new element_t); + current = current->next.get(); } if (q != buf) { @@ -146,8 +63,8 @@ element_t * format_t::parse_elements(const string& fmt) current->chars = string(buf, q); q = buf; - current->next = new element_t; - current = current->next; + current->next.reset(new element_t); + current = current->next.get(); } if (*p == '\\') { @@ -202,46 +119,17 @@ element_t * format_t::parse_elements(const string& fmt) current->chars = "%"; break; - case '(': { - ++p; - const char * b = p; - int depth = 1; - while (*p) { - if (*p == ')' && --depth == 0) - break; - else if (*p == '(') - ++depth; - p++; - } - if (*p != ')') - throw format_error("Missing ')'"); - - current->type = element_t::VALUE_EXPR; - - assert(! current->val_expr); - current->val_expr.parse(string(b, p)); - break; - } - + case '(': case '[': { - ++p; - const char * b = p; - int depth = 1; - while (*p) { - if (*p == ']' && --depth == 0) - break; - else if (*p == '[') - ++depth; - p++; - } - if (*p != ']') - throw format_error("Missing ']'"); - - current->type = element_t::DATE_STRING; - current->chars = string(b, p); + std::istringstream str(p); + current->type = element_t::EXPR; + current->expr.parse(str); + current->expr.set_text(string(p, p + str.tellg())); + p += str.tellg(); break; } +#if 0 case 'x': switch (*++p) { case 'B': current->type = element_t::XACT_BEG_POS; break; @@ -281,17 +169,20 @@ element_t * format_t::parse_elements(const string& fmt) case 'n': current->type = element_t::OPT_NOTE; break; case '|': current->type = element_t::SPACER; break; case '_': current->type = element_t::DEPTH_SPACER; break; +#endif } } +#if 0 END: +#endif if (q != buf) { if (! result.get()) { result.reset(new element_t); current = result.get(); } else { - current->next = new element_t; - current = current->next; + current->next.reset(new element_t); + current = current->next.get(); } current->type = element_t::STRING; current->chars = string(buf, q); @@ -301,20 +192,6 @@ element_t * format_t::parse_elements(const string& fmt) } namespace { - inline void mark_red(std::ostream& out, const element_t * elem) { - out.setf(std::ios::left); - out.width(0); - out << "\e[31m"; - - if (elem->flags & ELEMENT_ALIGN_LEFT) - out << std::left; - else - out << std::right; - - if (elem->min_width > 0) - out.width(elem->min_width); - } - inline void mark_plain(std::ostream& out) { out << "\e[0m"; } @@ -322,7 +199,7 @@ namespace { void format_t::format(std::ostream& out_str, scope_t& scope) const { - for (const element_t * elem = elements; elem; elem = elem->next) { + for (const element_t * elem = elements.get(); elem; elem = elem->next.get()) { std::ostringstream out; string name; bool ignore_max_width = false; @@ -340,10 +217,11 @@ void format_t::format(std::ostream& out_str, scope_t& scope) const out << elem->chars; break; - case element_t::AMOUNT: - out << scope.resolve("amount"); + case element_t::EXPR: + out << elem->expr.calc(scope); break; +#if 0 case element_t::ACCOUNT_FULLNAME: scope.resolve("account").dump(out, elem->min_width); break; @@ -351,12 +229,15 @@ void format_t::format(std::ostream& out_str, scope_t& scope) const scope.resolve("account_base").dump(out, elem->min_width); break; + case element_t::AMOUNT: + out << "a"; + //out << scope.resolve("amount"); + break; case element_t::TOTAL: out << "T"; //out << scope.resolve("total"); break; -#if 0 case element_t::VALUE_EXPR: { expr_t * calc; switch (elem->type) { @@ -579,13 +460,11 @@ void format_t::format(std::ostream& out_str, scope_t& scope) const if (details.xact) out << details.xact->end_line; break; -#endif case element_t::DATE_STRING: out << format_datetime(scope.resolve("date").as_datetime()); break; -#if 0 case element_t::COMPLETE_DATE_STRING: { datetime_t actual_date; datetime_t effective_date; @@ -670,13 +549,11 @@ void format_t::format(std::ostream& out_str, scope_t& scope) const out << temp; break; } -#endif case element_t::PAYEE: scope.resolve("payee").dump(out, elem->min_width); break; -#if 0 case element_t::OPT_NOTE: if (details.xact && details.xact->note) out << " ; "; @@ -731,12 +608,10 @@ void format_t::format(std::ostream& out_str, scope_t& scope) const } break; -#endif case element_t::SPACER: out << " "; break; -#if 0 case element_t::DEPTH_SPACER: for (const account_t * acct = details.account; acct; @@ -764,261 +639,90 @@ void format_t::format(std::ostream& out_str, scope_t& scope) const } } -format_xacts::format_xacts(std::ostream& _output_stream, - const string& format) - : output_stream(_output_stream), last_entry(NULL), last_xact(NULL) -{ - TRACE_CTOR(format_xacts, "std::ostream&, const string&"); - - const char * f = format.c_str(); - if (const char * p = std::strstr(f, "%/")) { - first_line_format.reset(string(f, 0, p - f)); - next_lines_format.reset(string(p + 2)); - } else { - first_line_format.reset(format); - next_lines_format.reset(format); - } -} - -void format_xacts::operator()(xact_t& xact) -{ - if (! xact_has_xdata(xact) || - ! (xact_xdata_(xact).dflags & XACT_DISPLAYED)) { - if (last_entry != xact.entry) { - first_line_format.format(output_stream, xact); - last_entry = xact.entry; - } - else if (last_xact && last_xact->date() != xact.date()) { - first_line_format.format(output_stream, xact); - } - else { - next_lines_format.format(output_stream, xact); - } - - xact_xdata(xact).dflags |= XACT_DISPLAYED; - last_xact = &xact; - } -} - -void format_entries::format_last_entry() -{ -#if 0 - bool first = true; - foreach (const transaction_t * xact, last_entry->xacts) { - if (xact_has_xdata(*xact) && - xact_xdata_(*xact).dflags & XACT_TO_DISPLAY) { - if (first) { - first_line_format.format(output_stream, details_t(*xact)); - first = false; - } else { - next_lines_format.format(output_stream, details_t(*xact)); - } - xact_xdata_(*xact).dflags |= XACT_DISPLAYED; - } - } -#endif -} - -void format_entries::operator()(xact_t& xact) -{ - xact_xdata(xact).dflags |= XACT_TO_DISPLAY; - - if (last_entry && xact.entry != last_entry) - format_last_entry(); - - last_entry = xact.entry; -} - -void print_entry(std::ostream& out, const entry_base_t& entry_base, - const string& prefix) +string format_t::truncate(const string& str, unsigned int width, + const bool is_account) { - string print_format; + const unsigned int len = str.length(); + if (len <= width) + return str; - if (dynamic_cast<const entry_t *>(&entry_base)) { - print_format = (prefix + "%D %X%C%P\n" + - prefix + " %-34A %12o\n%/" + - prefix + " %-34A %12o\n"); - } - else if (const auto_entry_t * entry = - dynamic_cast<const auto_entry_t *>(&entry_base)) { - out << "= " << entry->predicate.predicate.text() << '\n'; - print_format = prefix + " %-34A %12o\n"; - } - else if (const period_entry_t * entry = - dynamic_cast<const period_entry_t *>(&entry_base)) { - out << "~ " << entry->period_string << '\n'; - print_format = prefix + " %-34A %12o\n"; - } - else { - assert(false); - } + assert(width < 4095); -#if 0 - format_entries formatter(out, print_format); - walk_xacts(const_cast<xacts_list&>(entry_base.xacts), formatter); - formatter.flush(); + char buf[4096]; - clear_xact_xdata cleaner; - walk_xacts(const_cast<xacts_list&>(entry_base.xacts), cleaner); -#endif -} + switch (elision_style) { + case TRUNCATE_LEADING: + // This method truncates at the beginning. + std::strncpy(buf, str.c_str() + (len - width), width); + buf[0] = '.'; + buf[1] = '.'; + break; -bool disp_subaccounts_p(const account_t& account, - const optional<item_predicate<account_t> >& disp_pred, - const account_t *& to_show) -{ - bool display = false; -#if 0 - unsigned int counted = 0; - bool matches = disp_pred ? (*disp_pred)(account) : true; - bool computed = false; -#endif - value_t acct_total; - value_t result; + case TRUNCATE_MIDDLE: + // This method truncates in the middle. + std::strncpy(buf, str.c_str(), width / 2); + std::strncpy(buf + width / 2, + str.c_str() + (len - (width / 2 + width % 2)), + width / 2 + width % 2); + buf[width / 2 - 1] = '.'; + buf[width / 2] = '.'; + break; - to_show = NULL; + case ABBREVIATE: + if (is_account) { + std::list<string> parts; + string::size_type beg = 0; + for (string::size_type pos = str.find(':'); + pos != string::npos; + beg = pos + 1, pos = str.find(':', beg)) + parts.push_back(string(str, beg, pos - beg)); + parts.push_back(string(str, beg)); -#if 0 - for (accounts_map::value_type pair, account.accounts) { - if (disp_pred && ! (*disp_pred)(*pair.second)) - continue; + string result; + unsigned int newlen = len; + for (std::list<string>::iterator i = parts.begin(); + i != parts.end(); + i++) { + // Don't contract the last element + std::list<string>::iterator x = i; + if (++x == parts.end()) { + result += *i; + break; + } - compute_total(result, details_t(*pair.second)); - if (! computed) { - compute_total(acct_total, details_t(account)); - computed = true; - } + if (newlen > width) { + result += string(*i, 0, abbrev_length); + result += ":"; + newlen -= (*i).length() - abbrev_length; + } else { + result += *i; + result += ":"; + } + } - if ((result != acct_total) || counted > 0) { - display = matches; + if (newlen > width) { + // Even abbreviated its too big to show the last account, so + // abbreviate all but the last and truncate at the beginning. + std::strncpy(buf, result.c_str() + (result.length() - width), width); + buf[0] = '.'; + buf[1] = '.'; + } else { + std::strcpy(buf, result.c_str()); + } break; } - to_show = pair.second; - counted++; - } -#endif - - return display; -} - -bool display_account(const account_t& account, - const optional<item_predicate<account_t> >& disp_pred) -{ - // Never display an account that has already been displayed. - if (account_has_xdata(account) && - account_xdata_(account).dflags & ACCOUNT_DISPLAYED) - return false; - - // At this point, one of two possibilities exists: the account is a - // leaf which matches the predicate restrictions; or it is a parent - // and two or more children must be subtotaled; or it is a parent - // and its child has been hidden by the predicate. So first, - // determine if it is a parent that must be displayed regardless of - // the predicate. - - const account_t * account_to_show = NULL; - if (disp_subaccounts_p(account, disp_pred, account_to_show)) - return true; - - return (! account_to_show && - (! disp_pred || (*disp_pred)(const_cast<account_t&>(account)))); -} - -void format_accounts::operator()(account_t& account) -{ -#if 0 - if (display_account(account, disp_pred)) { - if (! account.parent) { - account_xdata(account).dflags |= ACCOUNT_TO_DISPLAY; - } else { - format.format(output_stream, details_t(account)); - account_xdata(account).dflags |= ACCOUNT_DISPLAYED; - } - } -#endif -} - -format_equity::format_equity(std::ostream& _output_stream, - const string& _format, - const string& display_predicate) - : output_stream(_output_stream), disp_pred(display_predicate) -{ -#if 0 - const char * f = _format.c_str(); - if (const char * p = std::strstr(f, "%/")) { - first_line_format.reset(string(f, 0, p - f)); - next_lines_format.reset(string(p + 2)); - } else { - first_line_format.reset(_format); - next_lines_format.reset(_format); - } - - entry_t header_entry; - header_entry.payee = "Opening Balances"; - header_entry._date = current_moment; - first_line_format.format(output_stream, details_t(header_entry)); -#endif -} - -void format_equity::flush() -{ -#if 0 - account_xdata_t xdata; - xdata.value = total; - xdata.value.negate(); - account_t summary(NULL, "Equity:Opening Balances"); - summary.data = &xdata; - - if (total.type() >= value_t::BALANCE) { - const balance_t * bal; - if (total.is_type(value_t::BALANCE)) - bal = &(total.as_balance()); - else if (total.is_type(value_t::BALANCE_PAIR)) - bal = &(total.as_balance_pair().quantity()); - else - assert(false); + // fall through... - for (balance_t::amounts_map::value_type pair, bal->amounts) { - xdata.value = pair.second; - xdata.value.negate(); - next_lines_format.format(output_stream, details_t(summary)); - } - } else { - next_lines_format.format(output_stream, details_t(summary)); + case TRUNCATE_TRAILING: + // This method truncates at the end (the default). + std::strncpy(buf, str.c_str(), width - 2); + buf[width - 2] = '.'; + buf[width - 1] = '.'; + break; } - output_stream.flush(); -#endif -} + buf[width] = '\0'; -void format_equity::operator()(account_t& account) -{ -#if 0 - if (display_account(account, disp_pred)) { - if (account_has_xdata(account)) { - value_t val = account_xdata_(account).value; - - if (val.type() >= value_t::BALANCE) { - const balance_t * bal; - if (val.is_type(value_t::BALANCE)) - bal = &(val.as_balance()); - else if (val.is_type(value_t::BALANCE_PAIR)) - bal = &(val.as_balance_pair().quantity()); - else - assert(false); - - for (balance_t::amounts_map::value_type pair, bal->amounts) { - account_xdata_(account).value = pair.second; - next_lines_format.format(output_stream, details_t(account)); - } - account_xdata_(account).value = val; - } else { - next_lines_format.format(output_stream, details_t(account)); - } - total += val; - } - account_xdata(account).dflags |= ACCOUNT_DISPLAYED; - } -#endif + return buf; } } // namespace ledger @@ -7,71 +7,58 @@ namespace ledger { -string truncated(const string& str, unsigned int width, - const int style = 2); - -string partial_account_name(const account_t& account, - const unsigned int start_depth); +DECLARE_EXCEPTION(format_error, std::runtime_error); +class format_t : public noncopyable +{ + struct element_t : public noncopyable + { #define ELEMENT_ALIGN_LEFT 0x01 #define ELEMENT_HIGHLIGHT 0x02 -struct element_t : public noncopyable -{ - enum kind_t { - STRING, - VALUE_EXPR, - SOURCE, - ENTRY_BEG_POS, - ENTRY_BEG_LINE, - ENTRY_END_POS, - ENTRY_END_LINE, - XACT_BEG_POS, - XACT_BEG_LINE, - XACT_END_POS, - XACT_END_LINE, - DATE_STRING, - COMPLETE_DATE_STRING, - CLEARED, - ENTRY_CLEARED, - CODE, - PAYEE, - OPT_ACCOUNT, - ACCOUNT_NAME, - ACCOUNT_FULLNAME, - AMOUNT, - OPT_AMOUNT, - TOTAL, - NOTE, - OPT_NOTE, - SPACER, - DEPTH_SPACER - }; + enum kind_t { + STRING, + EXPR, +#if 0 + SPACER, + DEPTH_SPACER +#endif + }; + + kind_t type; + unsigned char flags; + unsigned char min_width; + unsigned char max_width; + string chars; + expr_t expr; + + scoped_ptr<struct element_t> next; + + element_t() + : type(STRING), flags(false), min_width(0), max_width(0) { + TRACE_CTOR(element_t, ""); + } + ~element_t() { + TRACE_DTOR(element_t); + } - kind_t type; - unsigned char flags; - string chars; - unsigned char min_width; - unsigned char max_width; - expr_t val_expr; + friend inline void mark_red(std::ostream& out, const element_t * elem) { + out.setf(std::ios::left); + out.width(0); + out << "\e[31m"; - struct element_t * next; + if (elem->flags & ELEMENT_ALIGN_LEFT) + out << std::left; + else + out << std::right; - element_t() : type(STRING), flags(false), - min_width(0), max_width(0), next(NULL) { - TRACE_CTOR(element_t, ""); - } - - ~element_t() { - TRACE_DTOR(element_t); - if (next) checked_delete(next); // recursive, but not too deep - } -}; + if (elem->min_width > 0) + out.width(elem->min_width); + } + }; -struct format_t : public noncopyable -{ - string format_string; - element_t * elements; + string format_string; + scoped_ptr<element_t> elements; enum elision_style_t { TRUNCATE_TRAILING, @@ -81,146 +68,37 @@ struct format_t : public noncopyable }; static elision_style_t elision_style; - static int abbrev_length; + static int abbrev_length; - static bool ansi_codes; - static bool ansi_invert; + static bool ansi_codes; + static bool ansi_invert; - format_t() : elements(NULL) { +public: + format_t() { TRACE_CTOR(format_t, ""); } - format_t(const string& _format) : elements(NULL) { + format_t(const string& _format) { TRACE_CTOR(format_t, "const string&"); - reset(_format); + parse(_format); } ~format_t() { TRACE_DTOR(format_t); - if (elements) checked_delete(elements); } - void reset(const string& _format) { - if (elements) - checked_delete(elements); - elements = parse_elements(_format); + void parse(const string& _format) { + elements.reset(parse_elements(_format)); format_string = _format; } + void format(std::ostream& out, scope_t& scope) const; + +private: static element_t * parse_elements(const string& fmt); static string truncate(const string& str, unsigned int width, const bool is_account = false); - - void format(std::ostream& out, scope_t& scope) const; -}; - -class format_xacts : public item_handler<xact_t> -{ -protected: - std::ostream& output_stream; - format_t first_line_format; - format_t next_lines_format; - entry_t * last_entry; - xact_t * last_xact; - -public: - format_xacts(std::ostream& _output_stream, - const string& format); - ~format_xacts() throw() { - TRACE_DTOR(format_xacts); - } - - virtual void flush() { - output_stream.flush(); - } - virtual void operator()(xact_t& xact); -}; - -class format_entries : public format_xacts -{ - public: - format_entries(std::ostream& output_stream, const string& format) - : format_xacts(output_stream, format) { - TRACE_CTOR(format_entries, "std::ostream&, const string&"); - } - ~format_entries() throw() { - TRACE_DTOR(format_entries); - } - - virtual void format_last_entry(); - - virtual void flush() { - if (last_entry) { - format_last_entry(); - last_entry = NULL; - } - format_xacts::flush(); - } - virtual void operator()(xact_t& xact); -}; - -void print_entry(std::ostream& out, const entry_base_t& entry, - const string& prefix = ""); - -bool disp_subaccounts_p(const account_t& account, - const optional<item_predicate<account_t> >& disp_pred, - const account_t *& to_show); - -inline bool disp_subaccounts_p(const account_t& account) { - const account_t * temp; - return disp_subaccounts_p(account, none, temp); -} - -bool display_account(const account_t& account, - const optional<item_predicate<account_t> >& disp_pred); - -class format_accounts : public item_handler<account_t> -{ - std::ostream& output_stream; - - item_predicate<account_t> disp_pred; - - public: - format_t format; - - 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_accounts, "std::ostream&, const string&, const string&"); - } - ~format_accounts() throw() { - TRACE_DTOR(format_accounts); - } - - virtual void flush() { - output_stream.flush(); - } - - virtual void operator()(account_t& account); }; -class format_equity : public item_handler<account_t> -{ - std::ostream& output_stream; - format_t first_line_format; - format_t next_lines_format; - - item_predicate<account_t> disp_pred; - - mutable value_t total; - - public: - format_equity(std::ostream& _output_stream, - const string& _format, - const string& display_predicate); - - virtual void flush(); - virtual void operator()(account_t& account); -}; - -DECLARE_EXCEPTION(format_error, std::runtime_error); - } // namespace ledger #endif // _FORMAT_H @@ -138,7 +138,9 @@ static void endElement(void *userData, const char *name) // Add the new entry (what gnucash calls a 'xact') to the // journal if (! curr_journal->add_entry(curr_entry)) { +#if 0 print_entry(std::cerr, *curr_entry); +#endif have_error = "The above entry does not balance"; checked_delete(curr_entry); } else { @@ -456,4 +456,263 @@ expr_t::ptr_op_t report_t::lookup(const string& name) return session.lookup(name); } +// jww (2008-08-01): Find a home for this code + +format_xacts::format_xacts(std::ostream& _output_stream, + const string& format) + : output_stream(_output_stream), last_entry(NULL), last_xact(NULL) +{ + TRACE_CTOR(format_xacts, "std::ostream&, const string&"); + + const char * f = format.c_str(); + if (const char * p = std::strstr(f, "%/")) { + first_line_format.parse(string(f, 0, p - f)); + next_lines_format.parse(string(p + 2)); + } else { + first_line_format.parse(format); + next_lines_format.parse(format); + } +} + +void format_xacts::operator()(xact_t& xact) +{ + if (! xact_has_xdata(xact) || + ! (xact_xdata_(xact).dflags & XACT_DISPLAYED)) { + if (last_entry != xact.entry) { + first_line_format.format(output_stream, xact); + last_entry = xact.entry; + } + else if (last_xact && last_xact->date() != xact.date()) { + first_line_format.format(output_stream, xact); + } + else { + next_lines_format.format(output_stream, xact); + } + + xact_xdata(xact).dflags |= XACT_DISPLAYED; + last_xact = &xact; + } +} + +void format_entries::format_last_entry() +{ +#if 0 + bool first = true; + foreach (const transaction_t * xact, last_entry->xacts) { + if (xact_has_xdata(*xact) && + xact_xdata_(*xact).dflags & XACT_TO_DISPLAY) { + if (first) { + first_line_format.format(output_stream, details_t(*xact)); + first = false; + } else { + next_lines_format.format(output_stream, details_t(*xact)); + } + xact_xdata_(*xact).dflags |= XACT_DISPLAYED; + } + } +#endif +} + +void format_entries::operator()(xact_t& xact) +{ + xact_xdata(xact).dflags |= XACT_TO_DISPLAY; + + if (last_entry && xact.entry != last_entry) + format_last_entry(); + + last_entry = xact.entry; +} + +void print_entry(std::ostream& out, const entry_base_t& entry_base, + const string& prefix) +{ + string print_format; + + if (dynamic_cast<const entry_t *>(&entry_base)) { + print_format = (prefix + "%D %X%C%P\n" + + prefix + " %-34A %12o\n%/" + + prefix + " %-34A %12o\n"); + } + else if (const auto_entry_t * entry = + dynamic_cast<const auto_entry_t *>(&entry_base)) { + out << "= " << entry->predicate.predicate.text() << '\n'; + print_format = prefix + " %-34A %12o\n"; + } + else if (const period_entry_t * entry = + dynamic_cast<const period_entry_t *>(&entry_base)) { + out << "~ " << entry->period_string << '\n'; + print_format = prefix + " %-34A %12o\n"; + } + else { + assert(false); + } + +#if 0 + format_entries formatter(out, print_format); + walk_xacts(const_cast<xacts_list&>(entry_base.xacts), formatter); + formatter.flush(); + + clear_xact_xdata cleaner; + walk_xacts(const_cast<xacts_list&>(entry_base.xacts), cleaner); +#endif +} + +bool disp_subaccounts_p(const account_t& account, + const optional<item_predicate<account_t> >& disp_pred, + const account_t *& to_show) +{ + bool display = false; +#if 0 + unsigned int counted = 0; + bool matches = disp_pred ? (*disp_pred)(account) : true; + bool computed = false; +#endif + value_t acct_total; + value_t result; + + to_show = NULL; + +#if 0 + for (accounts_map::value_type pair, account.accounts) { + if (disp_pred && ! (*disp_pred)(*pair.second)) + continue; + + compute_total(result, details_t(*pair.second)); + if (! computed) { + compute_total(acct_total, details_t(account)); + computed = true; + } + + if ((result != acct_total) || counted > 0) { + display = matches; + break; + } + to_show = pair.second; + counted++; + } +#endif + + return display; +} + +bool display_account(const account_t& account, + const optional<item_predicate<account_t> >& disp_pred) +{ + // Never display an account that has already been displayed. + if (account_has_xdata(account) && + account_xdata_(account).dflags & ACCOUNT_DISPLAYED) + return false; + + // At this point, one of two possibilities exists: the account is a + // leaf which matches the predicate restrictions; or it is a parent + // and two or more children must be subtotaled; or it is a parent + // and its child has been hidden by the predicate. So first, + // determine if it is a parent that must be displayed regardless of + // the predicate. + + const account_t * account_to_show = NULL; + if (disp_subaccounts_p(account, disp_pred, account_to_show)) + return true; + + return (! account_to_show && + (! disp_pred || (*disp_pred)(const_cast<account_t&>(account)))); +} + +void format_accounts::operator()(account_t& account) +{ +#if 0 + if (display_account(account, disp_pred)) { + if (! account.parent) { + account_xdata(account).dflags |= ACCOUNT_TO_DISPLAY; + } else { + format.format(output_stream, details_t(account)); + account_xdata(account).dflags |= ACCOUNT_DISPLAYED; + } + } +#endif +} + +format_equity::format_equity(std::ostream& _output_stream, + const string& _format, + const string& display_predicate) + : output_stream(_output_stream), disp_pred(display_predicate) +{ +#if 0 + const char * f = _format.c_str(); + if (const char * p = std::strstr(f, "%/")) { + first_line_format.reset(string(f, 0, p - f)); + next_lines_format.reset(string(p + 2)); + } else { + first_line_format.reset(_format); + next_lines_format.reset(_format); + } + + entry_t header_entry; + header_entry.payee = "Opening Balances"; + header_entry._date = current_moment; + first_line_format.format(output_stream, details_t(header_entry)); +#endif +} + +void format_equity::flush() +{ +#if 0 + account_xdata_t xdata; + xdata.value = total; + xdata.value.negate(); + account_t summary(NULL, "Equity:Opening Balances"); + summary.data = &xdata; + + if (total.type() >= value_t::BALANCE) { + const balance_t * bal; + if (total.is_type(value_t::BALANCE)) + bal = &(total.as_balance()); + else if (total.is_type(value_t::BALANCE_PAIR)) + bal = &(total.as_balance_pair().quantity()); + else + assert(false); + + for (balance_t::amounts_map::value_type pair, bal->amounts) { + xdata.value = pair.second; + xdata.value.negate(); + next_lines_format.format(output_stream, details_t(summary)); + } + } else { + next_lines_format.format(output_stream, details_t(summary)); + } + output_stream.flush(); +#endif +} + +void format_equity::operator()(account_t& account) +{ +#if 0 + if (display_account(account, disp_pred)) { + if (account_has_xdata(account)) { + value_t val = account_xdata_(account).value; + + if (val.type() >= value_t::BALANCE) { + const balance_t * bal; + if (val.is_type(value_t::BALANCE)) + bal = &(val.as_balance()); + else if (val.is_type(value_t::BALANCE_PAIR)) + bal = &(val.as_balance_pair().quantity()); + else + assert(false); + + for (balance_t::amounts_map::value_type pair, bal->amounts) { + account_xdata_(account).value = pair.second; + next_lines_format.format(output_stream, details_t(account)); + } + account_xdata_(account).value = val; + } else { + next_lines_format.format(output_stream, details_t(account)); + } + total += val; + } + account_xdata(account).dflags |= ACCOUNT_DISPLAYED; + } +#endif +} + } // namespace ledger @@ -33,6 +33,7 @@ #define _REPORT_H #include "session.h" +#include "format.h" #include "walk.h" namespace ledger { @@ -207,9 +208,7 @@ public: // void eval(const string& expr) { -#if 0 - expr(expr).compile((xml::document_t *)NULL, this); -#endif + expr_t(expr).calc(*this); } value_t option_eval(call_scope_t& args) { eval(args[0].as_string()); @@ -230,12 +229,12 @@ public: return NULL_VALUE; } - value_t option_raw(call_scope_t& args) { + value_t option_raw(call_scope_t&) { raw_mode = true; return NULL_VALUE; } - value_t option_foo(call_scope_t& args) { + value_t option_foo(call_scope_t&) { std::cout << "This is foo" << std::endl; return NULL_VALUE; } @@ -302,6 +301,114 @@ public: string abbrev(const string& str, unsigned int width, const bool is_account); +// jww (2008-08-01): Where does this code belong? + +class format_xacts : public item_handler<xact_t> +{ +protected: + std::ostream& output_stream; + format_t first_line_format; + format_t next_lines_format; + entry_t * last_entry; + xact_t * last_xact; + +public: + format_xacts(std::ostream& _output_stream, + const string& format); + ~format_xacts() throw() { + TRACE_DTOR(format_xacts); + } + + virtual void flush() { + output_stream.flush(); + } + virtual void operator()(xact_t& xact); +}; + +class format_entries : public format_xacts +{ + public: + format_entries(std::ostream& output_stream, const string& format) + : format_xacts(output_stream, format) { + TRACE_CTOR(format_entries, "std::ostream&, const string&"); + } + ~format_entries() throw() { + TRACE_DTOR(format_entries); + } + + virtual void format_last_entry(); + + virtual void flush() { + if (last_entry) { + format_last_entry(); + last_entry = NULL; + } + format_xacts::flush(); + } + virtual void operator()(xact_t& xact); +}; + +void print_entry(std::ostream& out, const entry_base_t& entry, + const string& prefix = ""); + +bool disp_subaccounts_p(const account_t& account, + const optional<item_predicate<account_t> >& disp_pred, + const account_t *& to_show); + +inline bool disp_subaccounts_p(const account_t& account) { + const account_t * temp; + return disp_subaccounts_p(account, none, temp); +} + +bool display_account(const account_t& account, + const optional<item_predicate<account_t> >& disp_pred); + +class format_accounts : public item_handler<account_t> +{ + std::ostream& output_stream; + + item_predicate<account_t> disp_pred; + + public: + format_t format; + + 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_accounts, "std::ostream&, const string&, const string&"); + } + ~format_accounts() throw() { + TRACE_DTOR(format_accounts); + } + + virtual void flush() { + output_stream.flush(); + } + + virtual void operator()(account_t& account); +}; + +class format_equity : public item_handler<account_t> +{ + std::ostream& output_stream; + format_t first_line_format; + format_t next_lines_format; + + item_predicate<account_t> disp_pred; + + mutable value_t total; + + public: + format_equity(std::ostream& _output_stream, + const string& _format, + const string& display_predicate); + + virtual void flush(); + virtual void operator()(account_t& account); +}; + } // namespace ledger #endif // _REPORT_H @@ -2,7 +2,7 @@ #define _XML_H #include "journal.h" -#include "format.h" +#include "report.h" namespace ledger { |