/* * Copyright (c) 2003-2007, 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 "value.h" namespace ledger { intrusive_ptr value_t::true_value; intrusive_ptr value_t::false_value; void value_t::storage_t::destroy() { switch (type) { case AMOUNT: reinterpret_cast(data)->~amount_t(); break; case BALANCE: checked_delete(*reinterpret_cast(data)); break; case BALANCE_PAIR: checked_delete(*reinterpret_cast(data)); break; case STRING: reinterpret_cast(data)->~string(); break; case SEQUENCE: checked_delete(*reinterpret_cast(data)); break; case POINTER: reinterpret_cast(data)->~any(); break; default: break; } type = VOID; } void value_t::initialize() { #if 0 LOGGER("value.initialize"); #endif true_value = new storage_t; true_value->type = BOOLEAN; *reinterpret_cast(true_value->data) = true; false_value = new storage_t; false_value->type = BOOLEAN; *reinterpret_cast(false_value->data) = false; BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(bool)); BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(datetime_t)); BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(long)); BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(amount_t)); BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(balance_t *)); BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(balance_pair_t *)); BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(string)); BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(sequence_t *)); BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(boost::any)); #if 0 DEBUG_(std::setw(3) << std::right << sizeof(bool) << " sizeof(bool)"); DEBUG_(std::setw(3) << std::right << sizeof(datetime_t) << " sizeof(datetime_t)"); DEBUG_(std::setw(3) << std::right << sizeof(long) << " sizeof(long)"); DEBUG_(std::setw(3) << std::right << sizeof(amount_t) << " sizeof(amount_t)"); DEBUG_(std::setw(3) << std::right << sizeof(balance_t *) << " sizeof(balance_t *)"); DEBUG_(std::setw(3) << std::right << sizeof(balance_pair_t *) << " sizeof(balance_pair_t *)"); DEBUG_(std::setw(3) << std::right << sizeof(string) << " sizeof(string)"); DEBUG_(std::setw(3) << std::right << sizeof(sequence_t *) << " sizeof(sequence_t *)"); DEBUG_(std::setw(3) << std::right << sizeof(boost::any) << " sizeof(boost::any)"); #endif } void value_t::shutdown() { true_value = intrusive_ptr(); false_value = intrusive_ptr(); } void value_t::_dup() { assert(storage); if (storage->refc > 1) { storage = new storage_t(*storage.get()); // If the data referenced by storage is an allocated pointer, we // need to create a new object in order to achieve duplication. switch (storage->type) { case BALANCE: *(balance_t **) storage->data = new balance_t(**(balance_t **) storage->data); break; case BALANCE_PAIR: *(balance_pair_t **) storage->data = new balance_pair_t(**(balance_pair_t **) storage->data); break; case SEQUENCE: *(sequence_t **) storage->data = new sequence_t(**(sequence_t **) storage->data); break; default: break; // everything else has been duplicated } } } value_t::operator bool() const { switch (type()) { case BOOLEAN: return as_boolean(); case INTEGER: return as_long(); case DATETIME: return is_valid(as_datetime()); case AMOUNT: return as_amount(); case BALANCE: return as_balance(); case BALANCE_PAIR: return as_balance_pair(); case STRING: return ! as_string().empty(); case SEQUENCE: return ! as_sequence().empty(); case POINTER: return ! as_any_pointer().empty(); default: assert(false); break; } assert(false); return 0; } bool value_t::to_boolean() const { if (is_boolean()) { return as_boolean(); } else { value_t temp(*this); temp.in_place_cast(BOOLEAN); return temp.as_boolean(); } } long value_t::to_long() const { if (is_long()) { return as_long(); } else { value_t temp(*this); temp.in_place_cast(INTEGER); return temp.as_long(); } } datetime_t value_t::to_datetime() const { if (is_datetime()) { return as_datetime(); } else { value_t temp(*this); temp.in_place_cast(DATETIME); return temp.as_datetime(); } } amount_t value_t::to_amount() const { if (is_amount()) { return as_amount(); } else { value_t temp(*this); temp.in_place_cast(AMOUNT); return temp.as_amount(); } } balance_t value_t::to_balance() const { if (is_balance()) { return as_balance(); } else { value_t temp(*this); temp.in_place_cast(BALANCE); return temp.as_balance(); } } balance_pair_t value_t::to_balance_pair() const { if (is_balance_pair()) { return as_balance_pair(); } else { value_t temp(*this); temp.in_place_cast(BALANCE_PAIR); return temp.as_balance_pair(); } } string value_t::to_string() const { if (is_string()) { return as_string(); } else { value_t temp(*this); temp.in_place_cast(STRING); return temp.as_string(); } } value_t::sequence_t value_t::to_sequence() const { if (is_sequence()) { return as_sequence(); } else { value_t temp(*this); temp.in_place_cast(SEQUENCE); return temp.as_sequence(); } } void value_t::in_place_simplify() { LOGGER("amounts.values.simplify"); if (is_realzero()) { DEBUG_("Zeroing type " << type()); set_long(0L); return; } if (is_balance_pair() && (! as_balance_pair().cost || as_balance_pair().cost->is_realzero())) { DEBUG_("Reducing balance pair to balance"); in_place_cast(BALANCE); } if (is_balance() && as_balance().amounts.size() == 1) { DEBUG_("Reducing balance to amount"); in_place_cast(AMOUNT); } #if 0 if (is_amount() && ! as_amount().has_commodity() && as_amount().fits_in_long()) { DEBUG_("Reducing amount to integer"); in_place_cast(INTEGER); } #endif } value_t& value_t::operator+=(const value_t& val) { if (is_string()) { if (val.is_string()) as_string_lval() += val.as_string(); else as_string_lval() += val.to_string(); return *this; } else if (is_sequence()) { if (val.is_sequence()) { sequence_t& seq(as_sequence_lval()); seq.insert(seq.end(), val.as_sequence().begin(), val.as_sequence().end()); } else { as_sequence_lval().push_back(val); } return *this; } switch (type()) { case DATETIME: switch (val.type()) { case INTEGER: as_datetime_lval() += date_duration(val.as_long()); return *this; case AMOUNT: as_datetime_lval() += date_duration(val.as_amount().to_long()); return *this; default: break; } break; case INTEGER: switch (val.type()) { case INTEGER: as_long_lval() += val.as_long(); return *this; case AMOUNT: in_place_cast(AMOUNT); as_amount_lval() += val.as_amount(); return *this; case BALANCE: in_place_cast(BALANCE); as_balance_lval() += val.as_balance(); return *this; case BALANCE_PAIR: in_place_cast(BALANCE_PAIR); as_balance_pair_lval() += val.as_balance_pair(); return *this; default: break; } break; case AMOUNT: switch (val.type()) { case INTEGER: if (as_amount().has_commodity()) { in_place_cast(BALANCE); return *this += val; } else { as_amount_lval() += val.as_long(); return *this; } break; case AMOUNT: if (as_amount().commodity() != val.as_amount().commodity()) { in_place_cast(BALANCE); return *this += val; } else { as_amount_lval() += val.as_amount(); return *this; } break; case BALANCE: in_place_cast(BALANCE); as_balance_lval() += val.as_balance(); return *this; case BALANCE_PAIR: in_place_cast(BALANCE_PAIR); as_balance_pair_lval() += val.as_balance_pair(); return *this; default: break; } break; case BALANCE: switch (val.type()) { case INTEGER: as_balance_lval() += val.to_amount(); return *this; case AMOUNT: as_balance_lval() += val.as_amount(); return *this; case BALANCE: as_balance_lval() += val.as_balance(); return *this; case BALANCE_PAIR: in_place_cast(BALANCE_PAIR); as_balance_pair_lval() += val.as_balance_pair(); return *this; default: break; } break; case BALANCE_PAIR: switch (val.type()) { case INTEGER: as_balance_pair_lval() += val.to_amount(); return *this; case AMOUNT: as_balance_pair_lval() += val.as_amount(); return *this; case BALANCE: as_balance_pair_lval() += val.as_balance(); return *this; case BALANCE_PAIR: as_balance_pair_lval() += val.as_balance_pair(); return *this; default: break; } break; default: break; } throw_(value_error, "Cannot add " << label() << " to " << val.label()); return *this; } value_t& value_t::operator-=(const value_t& val) { if (is_sequence()) { sequence_t& seq(as_sequence_lval()); if (val.is_sequence()) { for (sequence_t::const_iterator i = val.as_sequence().begin(); i != val.as_sequence().end(); i++) { sequence_t::iterator j = std::find(seq.begin(), seq.end(), *i); if (j != seq.end()) seq.erase(j); } } else { sequence_t::iterator i = std::find(seq.begin(), seq.end(), val); if (i != seq.end()) seq.erase(i); } return *this; } switch (type()) { case DATETIME: switch (val.type()) { case INTEGER: as_datetime_lval() -= date_duration(val.as_long()); return *this; case AMOUNT: as_datetime_lval() -= date_duration(val.as_amount().to_long()); return *this; default: break; } break; case INTEGER: switch (val.type()) { case INTEGER: as_long_lval() -= val.as_long(); return *this; case AMOUNT: in_place_cast(AMOUNT); as_amount_lval() -= val.as_amount(); in_place_simplify(); return *this; case BALANCE: in_place_cast(BALANCE); as_balance_lval() -= val.as_balance(); in_place_simplify(); return *this; case BALANCE_PAIR: in_place_cast(BALANCE_PAIR); as_balance_pair_lval() -= val.as_balance_pair(); in_place_simplify(); return *this; default: break; } break; case AMOUNT: switch (val.type()) { case INTEGER: if (as_amount().has_commodity()) { in_place_cast(BALANCE); *this -= val; in_place_simplify(); return *this; } else { as_amount_lval() -= val.as_long(); in_place_simplify(); return *this; } break; case AMOUNT: if (as_amount().commodity() != val.as_amount().commodity()) { in_place_cast(BALANCE); *this -= val; in_place_simplify(); return *this; } else { as_amount_lval() -= val.as_amount(); in_place_simplify(); return *this; } break; case BALANCE: in_place_cast(BALANCE); as_balance_lval() -= val.as_balance(); in_place_simplify(); return *this; case BALANCE_PAIR: in_place_cast(BALANCE_PAIR); as_balance_pair_lval() -= val.as_balance_pair(); in_place_simplify(); return *this; default: break; } break; case BALANCE: switch (val.type()) { case INTEGER: as_balance_lval() -= val.to_amount(); in_place_simplify(); return *this; case AMOUNT: as_balance_lval() -= val.as_amount(); in_place_simplify(); return *this; case BALANCE: as_balance_lval() -= val.as_balance(); in_place_simplify(); return *this; case BALANCE_PAIR: in_place_cast(BALANCE_PAIR); as_balance_pair_lval() -= val.as_balance_pair(); in_place_simplify(); return *this; default: break; } break; case BALANCE_PAIR: switch (val.type()) { case INTEGER: as_balance_pair_lval() -= val.to_amount(); in_place_simplify(); return *this; case AMOUNT: as_balance_pair_lval() -= val.as_amount(); in_place_simplify(); return *this; case BALANCE: as_balance_pair_lval() -= val.as_balance(); in_place_simplify(); return *this; case BALANCE_PAIR: as_balance_pair_lval() -= val.as_balance_pair(); in_place_simplify(); return *this; default: break; } break; default: break; } throw_(value_error, "Cannot subtract " << label() << " from " << val.label()); return *this; } value_t& value_t::operator*=(const value_t& val) { if (is_string()) { string temp; long count = val.to_long(); for (long i = 0; i < count; i++) temp += as_string(); set_string(temp); return *this; } else if (is_sequence()) { value_t temp; long count = val.to_long(); for (long i = 0; i < count; i++) temp += as_sequence(); return *this = temp; } switch (type()) { case INTEGER: switch (val.type()) { case INTEGER: as_long_lval() *= val.as_long(); return *this; case AMOUNT: set_amount(val.as_amount() * as_long()); return *this; default: break; } break; case AMOUNT: switch (val.type()) { case INTEGER: as_amount_lval() *= val.as_long(); return *this; case AMOUNT: if (as_amount().commodity() == val.as_amount().commodity() || ! val.as_amount().has_commodity()) { as_amount_lval() *= val.as_amount(); return *this; } break; default: break; } break; case BALANCE: switch (val.type()) { case INTEGER: as_balance_lval() *= val.as_long(); return *this; case AMOUNT: if (! val.as_amount().has_commodity()) { as_balance_lval() *= val.as_amount(); return *this; } break; default: break; } break; case BALANCE_PAIR: switch (val.type()) { case INTEGER: as_balance_pair_lval() *= val.as_long(); return *this; case AMOUNT: if (! val.as_amount().has_commodity()) { as_balance_pair_lval() *= val.as_amount(); return *this; } break; default: break; } break; default: break; } throw_(value_error, "Cannot multiply " << label() << " with " << val.label()); return *this; } value_t& value_t::operator/=(const value_t& val) { switch (type()) { case INTEGER: switch (val.type()) { case INTEGER: as_long_lval() /= val.as_long(); return *this; case AMOUNT: set_amount(val.as_amount() / as_long()); return *this; default: break; } break; case AMOUNT: switch (val.type()) { case INTEGER: as_amount_lval() /= val.as_long(); return *this; case AMOUNT: if (as_amount().commodity() == val.as_amount().commodity() || ! val.as_amount().has_commodity()) { as_amount_lval() /= val.as_amount(); return *this; } break; default: break; } break; case BALANCE: switch (val.type()) { case INTEGER: as_balance_lval() /= val.as_long(); return *this; case AMOUNT: if (! val.as_amount().has_commodity()) { as_balance_lval() /= val.as_amount(); return *this; } break; default: break; } break; case BALANCE_PAIR: switch (val.type()) { case INTEGER: as_balance_pair_lval() /= val.as_long(); return *this; case AMOUNT: if (! val.as_amount().has_commodity()) { as_balance_pair_lval() /= val.as_amount(); return *this; } break; default: break; } break; default: break; } throw_(value_error, "Cannot divide " << label() << " by " << val.label()); return *this; } bool value_t::operator==(const value_t& val) const { switch (type()) { case BOOLEAN: if (val.is_boolean()) return as_boolean() == val.as_boolean(); break; case DATETIME: if (val.is_datetime()) return as_datetime() == val.as_datetime(); break; case INTEGER: switch (val.type()) { case INTEGER: return as_long() == val.as_long(); case AMOUNT: return val.as_amount() == to_amount(); case BALANCE: return val.as_balance() == to_amount(); case BALANCE_PAIR: return val.as_balance_pair() == to_amount(); default: break; } break; case AMOUNT: switch (val.type()) { case INTEGER: return as_amount() == val.as_long(); case AMOUNT: return as_amount() == val.as_amount(); case BALANCE: return val.as_balance() == as_amount(); case BALANCE_PAIR: return val.as_balance_pair() == as_amount(); default: break; } break; case BALANCE: switch (val.type()) { case INTEGER: return as_balance() == val.to_amount(); case AMOUNT: return as_balance() == val.as_amount(); case BALANCE: return as_balance() == val.as_balance(); case BALANCE_PAIR: return val.as_balance_pair() == as_balance(); default: break; } break; case BALANCE_PAIR: switch (val.type()) { case INTEGER: return as_balance_pair() == val.to_amount(); case AMOUNT: return as_balance_pair() == val.as_amount(); case BALANCE: return as_balance_pair() == val.as_balance(); case BALANCE_PAIR: return as_balance_pair() == val.as_balance_pair(); default: break; } break; case STRING: if (val.is_string()) return as_string() == val.as_string(); break; case SEQUENCE: if (val.is_sequence()) return as_sequence() == val.as_sequence(); break; default: break; } throw_(value_error, "Cannot compare " << label() << " to " << val.label()); return *this; } bool value_t::operator<(const value_t& val) const { switch (type()) { case DATETIME: if (val.is_datetime()) return as_datetime() < val.as_datetime(); break; case INTEGER: switch (val.type()) { case INTEGER: return as_long() < val.as_long(); case AMOUNT: return val.as_amount() < as_long(); default: break; } break; case AMOUNT: switch (val.type()) { case INTEGER: return as_amount() < val.as_long(); case AMOUNT: return as_amount() < val.as_amount(); default: break; } break; case STRING: if (val.is_string()) return as_string() < val.as_string(); break; default: break; } throw_(value_error, "Cannot compare " << label() << " to " << val.label()); return *this; } #if 0 bool value_t::operator>(const value_t& val) const { switch (type()) { case DATETIME: if (val.is_datetime()) return as_datetime() > val.as_datetime(); break; case INTEGER: switch (val.type()) { case INTEGER: return as_long() > val.as_long(); case AMOUNT: return val.as_amount() > as_long(); default: break; } break; case AMOUNT: switch (val.type()) { case INTEGER: return as_amount() > val.as_long(); case AMOUNT: return as_amount() > val.as_amount(); default: break; } break; case STRING: if (val.is_string()) return as_string() > val.as_string(); break; default: break; } throw_(value_error, "Cannot compare " << label() << " to " << val.label()); return *this; } #endif void value_t::in_place_cast(type_t cast_type) { if (type() == cast_type) return; if (cast_type == BOOLEAN) { set_boolean(bool(*this)); return; } else if (cast_type == SEQUENCE) { sequence_t temp; if (! is_null()) temp.push_back(*this); set_sequence(temp); return; } switch (type()) { case BOOLEAN: switch (cast_type) { case STRING: set_string(as_boolean() ? "true" : "false"); return; default: break; } break; case INTEGER: switch (cast_type) { case AMOUNT: set_amount(as_long()); return; case BALANCE: set_balance(to_amount()); return; case BALANCE_PAIR: set_balance_pair(to_amount()); return; case STRING: set_string(lexical_cast(as_long())); return; default: break; } break; case AMOUNT: switch (cast_type) { case INTEGER: set_long(as_amount().to_long()); return; case BALANCE: set_balance(as_amount()); return; case BALANCE_PAIR: set_balance_pair(as_amount()); return; case STRING: set_string(as_amount().to_string()); return; default: break; } break; case BALANCE: switch (cast_type) { case AMOUNT: { const balance_t& temp(as_balance()); if (temp.amounts.size() == 1) { set_amount((*temp.amounts.begin()).second); return; } else if (temp.amounts.size() == 0) { set_amount(0L); return; } else { throw_(value_error, "Cannot convert " << label() << " with multiple commodities to " << label(cast_type)); } break; } case BALANCE_PAIR: set_balance_pair(as_balance()); return; default: break; } break; case BALANCE_PAIR: switch (cast_type) { case AMOUNT: { const balance_t& temp(as_balance_pair().quantity()); if (temp.amounts.size() == 1) { set_amount((*temp.amounts.begin()).second); return; } else if (temp.amounts.size() == 0) { set_amount(0L); return; } else { throw_(value_error, "Cannot convert " << label() << " with multiple commodities to " << label(cast_type)); } break; } case BALANCE: set_balance(as_balance_pair().quantity()); return; default: break; } break; case STRING: switch (cast_type) { case INTEGER: { if (all(as_string(), is_digit())) { set_long(lexical_cast(as_string())); return; } else { throw_(value_error, "Cannot convert string '" << *this << "' to an integer"); } break; } case AMOUNT: set_amount(amount_t(as_string())); return; default: break; } break; default: break; } throw_(value_error, "Cannot convert " << label() << " to " << label(cast_type)); } void value_t::in_place_negate() { switch (type()) { case BOOLEAN: set_boolean(! as_boolean()); return; case INTEGER: set_long(- as_long()); return; case AMOUNT: as_amount_lval().in_place_negate(); return; case BALANCE: as_balance_lval().in_place_negate(); return; case BALANCE_PAIR: as_balance_pair_lval().in_place_negate(); return; default: break; } throw_(value_error, "Cannot negate " << label()); } bool value_t::is_realzero() const { switch (type()) { case BOOLEAN: return ! as_boolean(); case INTEGER: return as_long() == 0; case DATETIME: return ! is_valid(as_datetime()); case AMOUNT: return as_amount().is_realzero(); case BALANCE: return as_balance().is_realzero(); case BALANCE_PAIR: return as_balance_pair().is_realzero(); case STRING: return as_string().empty(); case SEQUENCE: return as_sequence().empty(); case POINTER: return as_any_pointer().empty(); default: assert(false); break; } assert(false); return true; } value_t value_t::value(const optional& moment) const { switch (type()) { case INTEGER: return *this; case AMOUNT: { if (optional val = as_amount().value(moment)) return *val; return false; } case BALANCE: { if (optional bal = as_balance().value(moment)) return *bal; return false; } case BALANCE_PAIR: { if (optional bal_pair = as_balance_pair().quantity().value(moment)) return *bal_pair; return false; } default: break; } throw_(value_error, "Cannot find the value of " << label()); return value_t(); } void value_t::in_place_reduce() { switch (type()) { case INTEGER: return; case AMOUNT: as_amount_lval().in_place_reduce(); return; case BALANCE: as_balance_lval().in_place_reduce(); return; case BALANCE_PAIR: as_balance_pair_lval().in_place_reduce(); return; default: break; } throw_(value_error, "Cannot reduce " << label()); } value_t value_t::abs() const { switch (type()) { case INTEGER: { long val = const_cast(*this).as_long_lval(); if (val < 0) return - val; return val; } case AMOUNT: return const_cast(*this).as_amount_lval().abs(); case BALANCE: return const_cast(*this).as_balance_lval().abs(); case BALANCE_PAIR: return const_cast(*this).as_balance_pair_lval().abs(); default: break; } throw_(value_error, "Cannot abs " << label()); return value_t(); } value_t value_t::round() const { switch (type()) { case INTEGER: return *this; case AMOUNT: return as_amount().round(); default: break; } throw_(value_error, "Cannot round " << label()); return value_t(); } value_t value_t::unround() const { switch (type()) { case INTEGER: return *this; case AMOUNT: return as_amount().unround(); default: break; } throw_(value_error, "Cannot unround " << label()); return value_t(); } value_t value_t::annotated_price() const { switch (type()) { case AMOUNT: { optional temp = as_amount().annotation_details().price; if (! temp) return false; return *temp; } default: break; } throw_(value_error, "Cannot find the annotated price of " << label()); return value_t(); } value_t value_t::annotated_date() const { switch (type()) { case DATETIME: return *this; case AMOUNT: { optional temp = as_amount().annotation_details().date; if (! temp) return false; return *temp; } default: break; } throw_(value_error, "Cannot find the annotated date of " << label()); return value_t(); } value_t value_t::annotated_tag() const { switch (type()) { case DATETIME: return *this; case AMOUNT: { optional temp = as_amount().annotation_details().tag; if (! temp) return false; return value_t(*temp, true); } default: break; } throw_(value_error, "Cannot find the annotated tag of " << label()); return value_t(); } value_t value_t::strip_annotations(const bool keep_price, const bool keep_date, const bool keep_tag) const { switch (type()) { case VOID: case BOOLEAN: case INTEGER: case DATETIME: case STRING: case POINTER: return *this; case SEQUENCE: { sequence_t temp; foreach (const value_t& value, as_sequence()) temp.push_back(value.strip_annotations(keep_price, keep_date, keep_tag)); return temp; } case AMOUNT: return as_amount().strip_annotations(keep_price, keep_date, keep_tag); case BALANCE: return as_balance().strip_annotations(keep_price, keep_date, keep_tag); case BALANCE_PAIR: return as_balance_pair().quantity().strip_annotations(keep_price, keep_date, keep_tag); default: assert(false); break; } assert(false); return value_t(); } value_t value_t::cost() const { switch (type()) { case INTEGER: case AMOUNT: case BALANCE: return *this; case BALANCE_PAIR: assert(as_balance_pair().cost); if (as_balance_pair().cost) return *(as_balance_pair().cost); else return as_balance_pair().quantity(); default: break; } throw_(value_error, "Cannot find the cost of " << label()); return value_t(); } value_t& value_t::add(const amount_t& amount, const optional& tcost) { switch (type()) { case INTEGER: case AMOUNT: if (tcost) { in_place_cast(BALANCE_PAIR); return add(amount, tcost); } else if ((is_amount() && as_amount().commodity() != amount.commodity()) || (! is_amount() && amount.commodity())) { in_place_cast(BALANCE); return add(amount, tcost); } else if (! is_amount()) { in_place_cast(AMOUNT); } *this += amount; break; case BALANCE: if (tcost) { in_place_cast(BALANCE_PAIR); return add(amount, tcost); } *this += amount; break; case BALANCE_PAIR: as_balance_pair_lval().add(amount, tcost); break; default: break; } throw_(value_error, "Cannot add an amount to " << label()); return *this; } void value_t::print(std::ostream& out, const int first_width, const int latter_width) const { switch (type()) { case VOID: out << "NULL"; break; case BOOLEAN: case DATETIME: case INTEGER: case AMOUNT: case STRING: case POINTER: // jww (2007-05-14): I need a version of this print just for XPath // expression, since amounts and strings need to be output with // special syntax. out << *this; break; case SEQUENCE: { out << '('; bool first = true; foreach (const value_t& value, as_sequence()) { if (first) first = false; else out << ", "; value.print(out, first_width, latter_width); } out << ')'; break; } case BALANCE: as_balance().print(out, first_width, latter_width); break; case BALANCE_PAIR: as_balance_pair().print(out, first_width, latter_width); break; default: assert(false); break; } } void value_context::describe(std::ostream& out) const throw() { if (! desc.empty()) out << desc << std::endl; balance_t * ptr = NULL; out << std::right; out.width(20); switch (bal.type()) { case value_t::BOOLEAN: out << (const_cast(bal).as_boolean_lval() ? "true" : "false"); break; case value_t::INTEGER: out << const_cast(bal).as_long_lval(); break; case value_t::DATETIME: out << const_cast(bal).as_datetime_lval(); break; case value_t::AMOUNT: out << const_cast(bal).as_amount_lval(); break; case value_t::BALANCE: ptr = &(const_cast(bal).as_balance_lval()); // fall through... case value_t::BALANCE_PAIR: if (! ptr) ptr = &(const_cast(bal).as_balance_pair_lval().quantity()); ptr->print(out, 20); break; default: assert(0); break; } out << std::endl; } } // namespace ledger