Simplified the format_t code so that it no longer references any particulars

about journal objects.  This is all done through value expressions now.
This commit is contained in:
John Wiegley 2008-08-01 01:37:20 -04:00
parent e5048ec71b
commit 567902b173
6 changed files with 550 additions and 600 deletions

532
format.cc
View file

@ -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;
namespace {
string partial_account_name(const account_t& account)
{
string name;
assert(width < 4095);
for (const account_t * acct = &account;
acct && acct->parent;
acct = acct->parent) {
if (account_has_xdata(*acct) &&
account_xdata_(*acct).dflags & ACCOUNT_DISPLAYED)
break;
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;
}
if (newlen > width) {
result += string(*i, 0, abbrev_length);
result += ":";
newlen -= (*i).length() - abbrev_length;
} else {
result += *i;
result += ":";
}
}
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)
string format_t::truncate(const string& str, unsigned int width,
const bool is_account)
{
TRACE_CTOR(format_xacts, "std::ostream&, const string&");
const unsigned int len = str.length();
if (len <= width)
return str;
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);
}
}
assert(width < 4095);
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);
}
char buf[4096];
xact_xdata(xact).dflags |= XACT_DISPLAYED;
last_xact = &xact;
}
}
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;
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));
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;
}
if (newlen > width) {
result += string(*i, 0, abbrev_length);
result += ":";
newlen -= (*i).length() - abbrev_length;
} else {
result += *i;
result += ":";
}
}
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;
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++;
// 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;
}
#endif
buf[width] = '\0';
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
return buf;
}
} // namespace ledger

238
format.h
View file

@ -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);
}
friend 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);
}
};
kind_t type;
unsigned char flags;
string chars;
unsigned char min_width;
unsigned char max_width;
expr_t val_expr;
struct element_t * next;
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
}
};
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

View file

@ -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 {

259
report.cc
View file

@ -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

117
report.h
View file

@ -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
xml.h
View file

@ -2,7 +2,7 @@
#define _XML_H
#include "journal.h"
#include "format.h"
#include "report.h"
namespace ledger {