#include "xml.h" #include "journal.h" #include "utils.h" namespace ledger { #if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE) static XML_Parser current_parser; static unsigned int count; static journal_t * curr_journal; static entry_t * curr_entry; static commodity_t * curr_comm; static string comm_flags; static transaction_t::state_t curr_state; static string data; static bool ignore; static string have_error; static void startElement(void *userData, const char *name, const char **attrs) { if (ignore) return; if (std::strcmp(name, "entry") == 0) { assert(! curr_entry); curr_entry = new entry_t; curr_state = transaction_t::UNCLEARED; } else if (std::strcmp(name, "transaction") == 0) { assert(curr_entry); curr_entry->add_transaction(new transaction_t); if (curr_state != transaction_t::UNCLEARED) curr_entry->transactions.back()->state = curr_state; } else if (std::strcmp(name, "commodity") == 0) { if (string(attrs[0]) == "flags") comm_flags = attrs[1]; } else if (std::strcmp(name, "total") == 0) { ignore = true; } } static void endElement(void *userData, const char *name) { if (ignore) { if (std::strcmp(name, "total") == 0) ignore = false; return; } if (std::strcmp(name, "entry") == 0) { assert(curr_entry); if (curr_journal->add_entry(curr_entry)) { count++; } else { account_t * acct = curr_journal->find_account(""); curr_entry->add_transaction(new transaction_t(acct)); if (curr_journal->add_entry(curr_entry)) { count++; } else { delete curr_entry; have_error = "Entry cannot be balanced"; } } curr_entry = NULL; } else if (std::strcmp(name, "en:date") == 0) { curr_entry->_date = parse_datetime(data); } else if (std::strcmp(name, "en:date_eff") == 0) { curr_entry->_date_eff = parse_datetime(data); } else if (std::strcmp(name, "en:code") == 0) { curr_entry->code = data; } else if (std::strcmp(name, "en:cleared") == 0) { curr_state = transaction_t::CLEARED; } else if (std::strcmp(name, "en:pending") == 0) { curr_state = transaction_t::PENDING; } else if (std::strcmp(name, "en:payee") == 0) { curr_entry->payee = data; } else if (std::strcmp(name, "tr:account") == 0) { curr_entry->transactions.back()->account = curr_journal->find_account(data); } else if (std::strcmp(name, "tr:cleared") == 0) { curr_entry->transactions.back()->state = transaction_t::CLEARED; } else if (std::strcmp(name, "tr:pending") == 0) { curr_entry->transactions.back()->state = transaction_t::PENDING; } else if (std::strcmp(name, "tr:virtual") == 0) { curr_entry->transactions.back()->flags |= TRANSACTION_VIRTUAL; } else if (std::strcmp(name, "tr:generated") == 0) { curr_entry->transactions.back()->flags |= TRANSACTION_AUTO; } else if (std::strcmp(name, "symbol") == 0) { assert(! curr_comm); curr_comm = amount_t::current_pool->find_or_create(data); assert(curr_comm); curr_comm->add_flags(COMMODITY_STYLE_SUFFIXED); if (! comm_flags.empty()) { for (string::size_type i = 0, l = comm_flags.length(); i < l; i++) { switch (comm_flags[i]) { case 'P': curr_comm->drop_flags(COMMODITY_STYLE_SUFFIXED); break; case 'S': curr_comm->add_flags(COMMODITY_STYLE_SEPARATED); break; case 'T': curr_comm->add_flags(COMMODITY_STYLE_THOUSANDS); break; case 'E': curr_comm->add_flags(COMMODITY_STYLE_EUROPEAN); break; } } } } #if 0 // jww (2006-03-02): !!! else if (std::strcmp(name, "price") == 0) { assert(curr_comm); amount_t * price = new amount_t(data); std::ostringstream symstr; symstr << curr_comm->symbol << " {" << *price << "}"; commodity_t * priced_comm = commodity_t::find_commodity(symstr.str(), true); priced_comm->price = price; priced_comm->base = curr_comm; curr_comm = priced_comm; } #endif else if (std::strcmp(name, "quantity") == 0) { curr_entry->transactions.back()->amount.parse(data); if (curr_comm) { string::size_type i = data.find('.'); if (i != string::npos) { int precision = data.length() - i - 1; if (precision > curr_comm->precision()) curr_comm->set_precision(precision); } curr_entry->transactions.back()->amount.set_commodity(*curr_comm); curr_comm = NULL; } } else if (std::strcmp(name, "tr:amount") == 0) { curr_comm = NULL; } } static void dataHandler(void *userData, const char *s, int len) { if (! ignore) data = string(s, len); } bool xml_parser_t::test(std::istream& in) const { char buf[80]; in.getline(buf, 79); if (std::strncmp(buf, "\n"; commodity_t& c = amount.commodity(); for (int i = 0; i < depth + 2; i++) out << ' '; out << "\n"; for (int i = 0; i < depth + 4; i++) out << ' '; #if 0 // jww (2006-03-02): !!! if (c.price) { out << "" << c.base->symbol << "\n"; for (int i = 0; i < depth + 4; i++) out << ' '; out << "\n"; xml_write_amount(out, *c.price, depth + 6); for (int i = 0; i < depth + 4; i++) out << ' '; out << "\n"; } else { out << "" << c.symbol << "\n"; } #endif for (int i = 0; i < depth + 2; i++) out << ' '; out << "\n"; for (int i = 0; i < depth + 2; i++) out << ' '; out << ""; out << amount.quantity_string() << "\n"; for (int i = 0; i < depth; i++) out << ' '; out << "\n"; } void xml_write_value(std::ostream& out, const value_t& value, const int depth = 0) { const balance_t * bal = NULL; for (int i = 0; i < depth; i++) out << ' '; out << "\n"; switch (value.type()) { case value_t::BOOLEAN: for (int i = 0; i < depth + 2; i++) out << ' '; out << "" << value.as_boolean() << "\n"; break; case value_t::INTEGER: for (int i = 0; i < depth + 2; i++) out << ' '; out << "" << value.as_long() << "\n"; break; case value_t::AMOUNT: xml_write_amount(out, value.as_amount(), depth + 2); break; case value_t::BALANCE: bal = &(value.as_balance()); // fall through... case value_t::BALANCE_PAIR: if (! bal) bal = &(value.as_balance_pair().quantity()); for (int i = 0; i < depth + 2; i++) out << ' '; out << "\n"; for (balance_t::amounts_map::const_iterator i = bal->amounts.begin(); i != bal->amounts.end(); i++) xml_write_amount(out, (*i).second, depth + 4); for (int i = 0; i < depth + 2; i++) out << ' '; out << "\n"; break; default: assert(false); break; } for (int i = 0; i < depth; i++) out << ' '; out << "\n"; } void output_xml_string(std::ostream& out, const string& str) { for (const char * s = str.c_str(); *s; s++) { switch (*s) { case '<': out << "<"; break; case '>': out << "&rt;"; break; case '&': out << "&"; break; default: out << *s; break; } } } void format_xml_entries::format_last_entry() { #if 0 // jww (2008-05-08): Need to format these dates output_stream << " \n" << " " << last_entry->_date.to_string("%Y/%m/%d") << "\n"; if (is_valid(last_entry->_date_eff)) output_stream << " " << last_entry->_date_eff.to_string("%Y/%m/%d") << "\n"; #endif if (! last_entry->code.empty()) { output_stream << " "; output_xml_string(output_stream, last_entry->code); output_stream << "\n"; } if (! last_entry->payee.empty()) { output_stream << " "; output_xml_string(output_stream, last_entry->payee); output_stream << "\n"; } bool first = true; for (transactions_list::const_iterator i = last_entry->transactions.begin(); i != last_entry->transactions.end(); i++) { if (transaction_has_xdata(**i) && transaction_xdata_(**i).dflags & TRANSACTION_TO_DISPLAY) { if (first) { output_stream << " \n"; first = false; } output_stream << " \n"; #if 0 // jww (2008-05-08): Need to format these if ((*i)->_date) output_stream << " " << (*i)->_date.to_string("%Y/%m/%d") << "\n"; if (is_valid((*i)->_date_eff)) output_stream << " " << (*i)->_date_eff.to_string("%Y/%m/%d") << "\n"; #endif if ((*i)->state == transaction_t::CLEARED) output_stream << " \n"; else if ((*i)->state == transaction_t::PENDING) output_stream << " \n"; if ((*i)->flags & TRANSACTION_VIRTUAL) output_stream << " \n"; if ((*i)->flags & TRANSACTION_AUTO) output_stream << " \n"; if ((*i)->account) { string name = (*i)->account->fullname(); if (name == "") name = "[TOTAL]"; else if (name == "") name = "[UNKNOWN]"; output_stream << " "; output_xml_string(output_stream, name); output_stream << "\n"; } output_stream << " \n"; if (transaction_xdata_(**i).dflags & TRANSACTION_COMPOUND) xml_write_value(output_stream, transaction_xdata_(**i).value, 10); else xml_write_value(output_stream, value_t((*i)->amount), 10); output_stream << " \n"; if ((*i)->cost) { output_stream << " \n"; xml_write_value(output_stream, value_t(*(*i)->cost), 10); output_stream << " \n"; } if (! (*i)->note.empty()) { output_stream << " "; output_xml_string(output_stream, (*i)->note); output_stream << "\n"; } if (show_totals) { output_stream << " \n"; xml_write_value(output_stream, transaction_xdata_(**i).total, 10); output_stream << " \n"; } output_stream << " \n"; transaction_xdata_(**i).dflags |= TRANSACTION_DISPLAYED; } } if (! first) output_stream << " \n"; output_stream << " \n"; } } // namespace ledger