diff options
author | John Wiegley <johnw@newartisans.com> | 2008-08-05 13:17:04 -0400 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2008-08-05 18:05:49 -0400 |
commit | f6f4a46cf5b14f9a2170cd6475958efbf320caec (patch) | |
tree | 05bc1defcdebc201de3dd10477483d906a842821 /src/value.cc | |
parent | b7970b29855563e4c67f85af8b31233eda80c22a (diff) | |
download | fork-ledger-f6f4a46cf5b14f9a2170cd6475958efbf320caec.tar.gz fork-ledger-f6f4a46cf5b14f9a2170cd6475958efbf320caec.tar.bz2 fork-ledger-f6f4a46cf5b14f9a2170cd6475958efbf320caec.zip |
Moved around most of the files so that source code is in src/, documentation
is in doc/, etc.
Diffstat (limited to 'src/value.cc')
-rw-r--r-- | src/value.cc | 1773 |
1 files changed, 1773 insertions, 0 deletions
diff --git a/src/value.cc b/src/value.cc new file mode 100644 index 00000000..ea52af1f --- /dev/null +++ b/src/value.cc @@ -0,0 +1,1773 @@ +/* + * 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 "value.h" +#include "binary.h" + +namespace ledger { + +intrusive_ptr<value_t::storage_t> value_t::true_value; +intrusive_ptr<value_t::storage_t> value_t::false_value; + +value_t::storage_t& value_t::storage_t::operator=(const value_t::storage_t& rhs) +{ + type = rhs.type; + + switch (type) { + case DATETIME: + new(reinterpret_cast<datetime_t *>(data)) + datetime_t(*reinterpret_cast<datetime_t *> + (const_cast<char *>(rhs.data))); + break; + + case DATE: + new(reinterpret_cast<date_t *>(data)) + date_t(*reinterpret_cast<date_t *>(const_cast<char *>(rhs.data))); + break; + + case AMOUNT: + new(reinterpret_cast<amount_t *>(data)) + amount_t(*reinterpret_cast<amount_t *> + (const_cast<char *>(rhs.data))); + break; + + case BALANCE: + *reinterpret_cast<balance_t **>(data) = + new balance_t(**reinterpret_cast<balance_t **> + (const_cast<char *>(rhs.data))); + break; + + case BALANCE_PAIR: + *reinterpret_cast<balance_pair_t **>(data) = + new balance_pair_t(**reinterpret_cast<balance_pair_t **> + (const_cast<char *>(rhs.data))); + break; + + case STRING: + new(reinterpret_cast<string *>(data)) + string(*reinterpret_cast<string *>(const_cast<char *>(rhs.data))); + break; + + case SEQUENCE: + *reinterpret_cast<sequence_t **>(data) = + new sequence_t(**reinterpret_cast<sequence_t **> + (const_cast<char *>(rhs.data))); + break; + + default: + // The rest are fundamental types, which can be copied using + // std::memcpy + std::memcpy(data, rhs.data, sizeof(data)); + break; + } + + return *this; +} + +void value_t::storage_t::destroy() +{ + switch (type) { + case AMOUNT: + reinterpret_cast<amount_t *>(data)->~amount_t(); + break; + case BALANCE: + checked_delete(*reinterpret_cast<balance_t **>(data)); + break; + case BALANCE_PAIR: + checked_delete(*reinterpret_cast<balance_pair_t **>(data)); + break; + case STRING: + reinterpret_cast<string *>(data)->~string(); + break; + case SEQUENCE: + checked_delete(*reinterpret_cast<sequence_t **>(data)); + break; + case POINTER: + reinterpret_cast<boost::any *>(data)->~any(); + break; + + default: + break; + } + type = VOID; +} + +void value_t::initialize() +{ + LOGGER("value.initialize"); + + true_value = new storage_t; + true_value->type = BOOLEAN; + *reinterpret_cast<bool *>(true_value->data) = true; + + false_value = new storage_t; + false_value->type = BOOLEAN; + *reinterpret_cast<bool *>(false_value->data) = false; + +#if 0 + BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(bool)); + BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(datetime_t)); + BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(date_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)); +#endif + + 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(date_t) + << " sizeof(date_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)"); +} + +void value_t::shutdown() +{ + true_value = intrusive_ptr<storage_t>(); + false_value = intrusive_ptr<storage_t>(); +} + +void value_t::_dup() +{ + assert(storage); + if (storage->refc > 1) + storage = new storage_t(*storage.get()); +} + +value_t::operator bool() const +{ + switch (type()) { + case BOOLEAN: + return as_boolean(); + case DATETIME: + return is_valid(as_datetime()); + case DATE: + return is_valid(as_date()); + case INTEGER: + return as_long(); + 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(); + } +} + +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(); + } +} + +date_t value_t::to_date() const +{ + if (is_date()) { + return as_date(); + } else { + value_t temp(*this); + temp.in_place_cast(DATE); + return temp.as_date(); + } +} + +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(); + } +} + +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 " << static_cast<int>(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"); + DEBUG("ledger.value.reduce", "as a balance it looks like: " << *this); + in_place_cast(AMOUNT); + DEBUG("ledger.value.reduce", "as an amount it looks like: " << *this); + } + +#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 DATE: + switch (val.type()) { + case INTEGER: + as_date_lval() += date_duration_t(val.as_long()); + return *this; + case AMOUNT: + as_date_lval() += date_duration_t(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 " << val.label() << " to " << 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()) { + foreach (const value_t& v, val.as_sequence()) { + sequence_t::iterator j = std::find(seq.begin(), seq.end(), v); + 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 DATE: + switch (val.type()) { + case INTEGER: + as_date_lval() -= date_duration_t(val.as_long()); + return *this; + case AMOUNT: + as_date_lval() -= date_duration_t(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 " << val.label() << " from " << 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 VOID: + return val.type() == VOID; + + 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 DATE: + if (val.is_date()) + return as_date() == val.as_date(); + 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 DATE: + if (val.is_date()) + return as_date() < val.as_date(); + 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 DATE: + if (val.is_date()) + return as_date() > val.as_date(); + 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<string>(as_long())); + return; + default: + break; + } + break; + + case AMOUNT: { + const amount_t& amt(as_amount()); + switch (cast_type) { + case INTEGER: + if (amt.is_null()) + set_long(0L); + else + set_long(as_amount().to_long()); + return; + case BALANCE: + if (amt.is_null()) + set_balance(balance_t()); + else + set_balance(as_amount()); // creates temporary + return; + case BALANCE_PAIR: + if (amt.is_null()) + set_balance_pair(balance_pair_t()); + else + set_balance_pair(as_amount()); // creates temporary + return; + case STRING: + if (amt.is_null()) + set_string(""); + else + 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) { + // Because we are changing the current balance value to an amount + // value, and because set_amount takes a reference (and that memory is + // about to be repurposed), we must pass in a copy. + set_amount(amount_t((*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(amount_t((*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: + // A temporary is required, becaues set_balance is going to wipe us out + // before assigned the value passed in. + set_balance(balance_t(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<long>(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: + case DATETIME: + set_long(- as_long()); + return; + case DATE: + 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 DATE: + return ! is_valid(as_date()); + 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; +} + +bool value_t::is_zero() const +{ + switch (type()) { + case BOOLEAN: + return ! as_boolean(); + case INTEGER: + return as_long() == 0; + case DATETIME: + return ! is_valid(as_datetime()); + case DATE: + return ! is_valid(as_date()); + case AMOUNT: + return as_amount().is_zero(); + case BALANCE: + return as_balance().is_zero(); + case BALANCE_PAIR: + return as_balance_pair().is_zero(); + 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<datetime_t>& moment) const +{ + switch (type()) { + case INTEGER: + return *this; + + case AMOUNT: { + if (optional<amount_t> val = as_amount().value(moment)) + return *val; + return false; + } + case BALANCE: { + if (optional<balance_t> bal = as_balance().value(moment)) + return *bal; + return false; + } + case BALANCE_PAIR: { + if (optional<balance_t> 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 NULL_VALUE; +} + +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 = as_long(); + if (val < 0) + return - val; + return val; + } + case AMOUNT: + return as_amount().abs(); + case BALANCE: + return as_balance().abs(); + case BALANCE_PAIR: + return as_balance_pair().abs(); + default: + break; + } + + throw_(value_error, "Cannot abs " << label()); + return NULL_VALUE; +} + +value_t value_t::round() const +{ + switch (type()) { + case INTEGER: + return *this; + case AMOUNT: + return as_amount().round(); + case BALANCE: + return as_balance().round(); + case BALANCE_PAIR: + return as_balance_pair().round(); + default: + break; + } + + throw_(value_error, "Cannot round " << label()); + return NULL_VALUE; +} + +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 NULL_VALUE; +} + +#if 0 +value_t value_t::annotated_price() const +{ + switch (type()) { + case AMOUNT: { + optional<amount_t> 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 NULL_VALUE; +} + +value_t value_t::annotated_date() const +{ + switch (type()) { + case DATETIME: + return *this; + case DATE: + return *this; + + case AMOUNT: { + optional<datetime_t> 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 NULL_VALUE; +} + +value_t value_t::annotated_tag() const +{ + switch (type()) { + case AMOUNT: { + optional<string> temp = as_amount().annotation_details().tag; + if (! temp) + return false; + return string_value(*temp); + } + + case STRING: + return *this; + + default: + break; + } + + throw_(value_error, "Cannot find the annotated tag of " << label()); + return NULL_VALUE; +} +#endif + +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 DATE: + 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 NULL_VALUE; +} + +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 NULL_VALUE; +} + +value_t& value_t::add(const amount_t& amount, const optional<amount_t>& 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); + } + return *this += amount; + + case BALANCE: + if (tcost) { + in_place_cast(BALANCE_PAIR); + return add(amount, tcost); + } + return *this += amount; + + case BALANCE_PAIR: + as_balance_pair_lval().add(amount, tcost); + return *this; + + default: + break; + } + + throw_(value_error, "Cannot add an amount to " << label()); + return *this; +} + +void value_t::dump(std::ostream& out, const int first_width, + const int latter_width) const +{ + switch (type()) { + case VOID: + out << "VOID"; + break; + + case BOOLEAN: + out << as_boolean(); + break; + + case DATETIME: + out << format_datetime(as_datetime()); + break; + + case DATE: + out << format_date(as_date()); + break; + + case INTEGER: + out << as_long(); + break; + + case AMOUNT: + out << as_amount(); + break; + + case STRING: + out << as_string(); + break; + + case POINTER: + out << boost::unsafe_any_cast<const void *>(&as_any_pointer()); + break; + + case SEQUENCE: { + out << '('; + bool first = true; + foreach (const value_t& value, as_sequence()) { + if (first) + first = false; + else + out << ", "; + + value.dump(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_t::print(std::ostream& out, const bool relaxed) const +{ + switch (type()) { + case VOID: + out << ""; + break; + + case BOOLEAN: + if (as_boolean()) + out << "true"; + else + out << "false"; + break; + + case INTEGER: + out << as_long(); + break; + + case AMOUNT: + if (! relaxed) + out << '{'; + out << as_amount(); + if (! relaxed) + out << '}'; + break; + + case BALANCE: + case BALANCE_PAIR: + assert(false); + break; + + case DATETIME: + assert(false); + break; + case DATE: + out << '[' << format_date(as_date()) << ']'; + break; + + case STRING: + out << '"' << as_string() << '"'; + break; + + case POINTER: + assert(false); + break; + + case SEQUENCE: { + out << '('; + bool first = true; + foreach (const value_t& value, as_sequence()) { + if (first) + first = false; + else + out << ", "; + + value.print(out, relaxed); + } + out << ')'; + break; + } + } +} + +void value_t::read(const char *& data) +{ + switch (static_cast<value_t::type_t>(binary::read_long<int>(data))) { + case BOOLEAN: + set_boolean(binary::read_bool(data)); + break; + case INTEGER: + set_long(binary::read_long<unsigned long>(data)); + break; + case DATETIME: +#if 0 + // jww (2008-04-22): I need to record and read a datetime_t directly + set_datetime(read_long<unsigned long>(data)); +#endif + break; + case DATE: +#if 0 + // jww (2008-04-22): I need to record and read a date_t directly + set_date(read_long<unsigned long>(data)); +#endif + break; + case AMOUNT: { + amount_t temp; + temp.read(data); + set_amount(temp); + break; + } + default: + break; + } + + throw_(value_error, "Cannot read " << label() << " from a stream"); +} + +void value_t::write(std::ostream& out) const +{ + binary::write_long(out, static_cast<int>(type())); + + switch (type()) { + case BOOLEAN: + binary::write_bool(out, as_boolean()); + break; + case INTEGER: + binary::write_long(out, as_long()); + break; + case DATETIME: +#if 0 + binary::write_number(out, as_datetime()); +#endif + break; + case DATE: +#if 0 + binary::write_number(out, as_date()); +#endif + break; + case AMOUNT: + as_amount().write(out); + break; + default: + break; + } + + throw_(value_error, "Cannot read " << label() << " to a stream"); +} + +bool value_t::valid() const +{ + switch (type()) { + case AMOUNT: + return as_amount().valid(); + case BALANCE: + return as_balance().valid(); + case BALANCE_PAIR: + return as_balance_pair().valid(); + default: + break; + } + return true; +} + +} // namespace ledger |