/* * Copyright (c) 2003-2008, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #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 xact_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 = xact_t::UNCLEARED; } else if (std::strcmp(name, "xact") == 0) { assert(curr_entry); curr_entry->add_xact(new xact_t); if (curr_state != xact_t::UNCLEARED) curr_entry->xacts.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_xact(new xact_t(acct)); if (curr_journal->add_entry(curr_entry)) { count++; } else { checked_delete(curr_entry); have_error = "Entry cannot be balanced"; } } curr_entry = NULL; } else if (std::strcmp(name, "en:date") == 0) { curr_entry->_date = parse_date(data); } else if (std::strcmp(name, "en:date_eff") == 0) { curr_entry->_date_eff = parse_date(data); } else if (std::strcmp(name, "en:code") == 0) { curr_entry->code = data; } else if (std::strcmp(name, "en:cleared") == 0) { curr_state = xact_t::CLEARED; } else if (std::strcmp(name, "en:pending") == 0) { curr_state = xact_t::PENDING; } else if (std::strcmp(name, "en:payee") == 0) { curr_entry->payee = data; } else if (std::strcmp(name, "tr:account") == 0) { curr_entry->xacts.back()->account = curr_journal->find_account(data); } else if (std::strcmp(name, "tr:cleared") == 0) { curr_entry->xacts.back()->state = xact_t::CLEARED; } else if (std::strcmp(name, "tr:pending") == 0) { curr_entry->xacts.back()->state = xact_t::PENDING; } else if (std::strcmp(name, "tr:virtual") == 0) { curr_entry->xacts.back()->add_flags(XACT_VIRTUAL); } else if (std::strcmp(name, "tr:generated") == 0) { curr_entry->xacts.back()->add_flags(XACT_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->xacts.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->xacts.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"; foreach (const balance_t::amounts_map::value_type& pair, bal->amounts) xml_write_amount(out, pair.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) { 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; foreach (xact_t * xact, last_entry->xacts) { if (xact->has_xdata() && xact->xdata().has_flags(XACT_EXT_TO_DISPLAY)) { if (first) { output_stream << " \n"; first = false; } output_stream << " \n"; #if 0 // jww (2008-05-08): Need to format these if (xact->_date) output_stream << " " << xact->_date.to_string("%Y/%m/%d") << "\n"; if (is_valid(xact->_date_eff)) output_stream << " " << xact->_date_eff.to_string("%Y/%m/%d") << "\n"; #endif if (xact->state == xact_t::CLEARED) output_stream << " \n"; else if (xact->state == xact_t::PENDING) output_stream << " \n"; if (xact->has_flags(XACT_VIRTUAL)) output_stream << " \n"; if (xact->has_flags(XACT_AUTO)) output_stream << " \n"; if (xact->account) { string name = xact->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 (xact->xdata().has_flags(XACT_EXT_COMPOUND)) xml_write_value(output_stream, xact->xdata().value, 10); else xml_write_value(output_stream, value_t(xact->amount), 10); output_stream << " \n"; if (xact->cost) { output_stream << " \n"; xml_write_value(output_stream, value_t(*xact->cost), 10); output_stream << " \n"; } if (xact->note) { output_stream << " "; output_xml_string(output_stream, *xact->note); output_stream << "\n"; } if (show_totals) { output_stream << " \n"; xml_write_value(output_stream, xact->xdata().total, 10); output_stream << " \n"; } output_stream << " \n"; xact->xdata().add_flags(XACT_EXT_DISPLAYED); } } if (! first) output_stream << " \n"; output_stream << " \n"; } } // namespace ledger