summaryrefslogtreecommitdiff
path: root/src/value.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/value.cc')
-rw-r--r--src/value.cc1864
1 files changed, 1864 insertions, 0 deletions
diff --git a/src/value.cc b/src/value.cc
new file mode 100644
index 00000000..cce4c4e8
--- /dev/null
+++ b/src/value.cc
@@ -0,0 +1,1864 @@
+/*
+ * Copyright (c) 2003-2009, 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 <system.hh>
+
+#include "value.h"
+#include "commodity.h"
+#include "annotate.h"
+#include "pool.h"
+#include "unistring.h" // for justify()
+
+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 BALANCE:
+ data = new balance_t(*boost::get<balance_t *>(rhs.data));
+ break;
+ case SEQUENCE:
+ data = new sequence_t(*boost::get<sequence_t *>(rhs.data));
+ break;
+
+ default:
+ data = rhs.data;
+ break;
+ }
+
+ return *this;
+}
+
+void value_t::initialize()
+{
+ true_value = new storage_t;
+ true_value->type = BOOLEAN;
+ true_value->data = true;
+
+ false_value = new storage_t;
+ false_value->type = BOOLEAN;
+ false_value->data = false;
+}
+
+void value_t::shutdown()
+{
+ true_value = intrusive_ptr<storage_t>();
+ false_value = intrusive_ptr<storage_t>();
+}
+
+value_t::operator bool() const
+{
+ switch (type()) {
+ case VOID:
+ return false;
+ 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 STRING:
+ return ! as_string().empty();
+ case MASK: {
+ std::ostringstream out;
+ out << *this;
+ throw_(value_error,
+ _("Cannot determine truth of %1 (did you mean 'account =~ %2'?)")
+ << label() << out.str());
+ }
+ case SEQUENCE:
+ if (! as_sequence().empty()) {
+ foreach (const value_t& value, as_sequence()) {
+ if (value)
+ return true;
+ }
+ }
+ return false;
+ case SCOPE:
+ return as_scope() != NULL;
+ default:
+ break;
+ }
+
+ throw_(value_error, _("Cannot determine truth of %1") << label());
+ return false;
+}
+
+void value_t::set_type(type_t new_type)
+{
+ if (new_type == VOID) {
+#if BOOST_VERSION >= 103700
+ storage.reset();
+#else
+ storage = intrusive_ptr<storage_t>();
+#endif
+ } else {
+ if (! storage || storage->refc > 1)
+ storage = new storage_t;
+ else
+ storage->destroy();
+ storage->type = new_type;
+ }
+}
+
+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();
+ }
+}
+
+int value_t::to_int() const
+{
+ if (is_long()) {
+ return static_cast<int>(as_long());
+ } else {
+ value_t temp(*this);
+ temp.in_place_cast(INTEGER);
+ return static_cast<int>(temp.as_long());
+ }
+}
+
+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();
+ }
+}
+
+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();
+ }
+}
+
+mask_t value_t::to_mask() const
+{
+ if (is_mask()) {
+ return as_mask();
+ } else {
+ value_t temp(*this);
+ temp.in_place_cast(MASK);
+ return temp.as_mask();
+ }
+}
+
+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()
+{
+#if defined(DEBUG_ON)
+ LOGGER("value.simplify");
+#endif
+
+ if (is_realzero()) {
+ DEBUG_("Zeroing type " << static_cast<int>(type()));
+ set_long(0L);
+ return;
+ }
+
+ if (is_balance() && as_balance().single_amount()) {
+ DEBUG_("Reducing balance to amount");
+ DEBUG_("as a balance it looks like: " << *this);
+ in_place_cast(AMOUNT);
+ DEBUG_("as an amount it looks like: " << *this);
+ }
+
+#ifdef REDUCE_TO_INTEGER // this is off by default
+ 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::number() const
+{
+ switch (type()) {
+ case VOID:
+ return 0L;
+ case BOOLEAN:
+ return as_boolean() ? 1L : 0L;
+ case INTEGER:
+ return as_long();
+ case AMOUNT:
+ return as_amount().number();
+ case BALANCE:
+ return as_balance().number();
+ case SEQUENCE:
+ if (! as_sequence().empty()) {
+ value_t temp;
+ foreach (const value_t& value, as_sequence())
+ temp += value.number();
+ return temp;
+ }
+ break;
+ default:
+ break;
+ }
+
+ throw_(value_error, _("Cannot determine numeric value of %1") << label());
+ return false;
+}
+
+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()) {
+ if (size() == val.size()) {
+ sequence_t::iterator i = begin();
+ sequence_t::const_iterator j = val.begin();
+
+ for (; i != end(); i++, j++)
+ *i += *j;
+ } else {
+ throw_(value_error, _("Cannot add sequences of different lengths"));
+ }
+ } else {
+ as_sequence_lval().push_back(val);
+ }
+ return *this;
+ }
+
+ switch (type()) {
+ case DATETIME:
+ switch (val.type()) {
+ case INTEGER:
+ as_datetime_lval() +=
+ time_duration_t(0, 0, static_cast<time_duration_t::sec_type>(val.as_long()));
+ return *this;
+ case AMOUNT:
+ as_datetime_lval() +=
+ time_duration_t(0, 0, static_cast<time_duration_t::sec_type>
+ (val.as_amount().to_long()));
+ return *this;
+ default:
+ break;
+ }
+ break;
+
+ case DATE:
+ switch (val.type()) {
+ case INTEGER:
+ as_date_lval() += gregorian::date_duration(val.as_long());
+ return *this;
+ case AMOUNT:
+ as_date_lval() += gregorian::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:
+ if (val.as_amount().has_commodity()) {
+ in_place_cast(BALANCE);
+ return *this += val;
+ }
+ 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;
+ 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;
+
+ 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;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ throw_(value_error, _("Cannot add %1 to %2") << val.label() << 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()) {
+ if (size() == val.size()) {
+ sequence_t::iterator i = begin();
+ sequence_t::const_iterator j = val.begin();
+
+ for (; i != end(); i++, j++)
+ *i -= *j;
+ } else {
+ throw_(value_error, _("Cannot subtract sequences of different lengths"));
+ }
+ } 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() -=
+ time_duration_t(0, 0, static_cast<time_duration_t::sec_type>(val.as_long()));
+ return *this;
+ case AMOUNT:
+ as_datetime_lval() -=
+ time_duration_t(0, 0, static_cast<time_duration_t::sec_type>
+ (val.as_amount().to_long()));
+ return *this;
+ default:
+ break;
+ }
+ break;
+
+ case DATE:
+ switch (val.type()) {
+ case INTEGER:
+ as_date_lval() -= gregorian::date_duration(val.as_long());
+ return *this;
+ case AMOUNT:
+ as_date_lval() -= gregorian::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;
+ 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;
+
+ 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;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ throw_(value_error, _("Cannot subtract %1 from %2") << val.label() << 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:
+ as_amount_lval() *= val.as_amount();
+ return *this;
+ case BALANCE:
+ if (val.as_balance().single_amount()) {
+ as_amount_lval() *= val.simplified().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 (as_balance().single_amount()) {
+ in_place_simplify();
+ as_amount_lval() *= val.as_amount();
+ return *this;
+ }
+ else if (! val.as_amount().has_commodity()) {
+ as_balance_lval() *= val.as_amount();
+ return *this;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ DEBUG("value.multiply.error", "Left: " << *this);
+ DEBUG("value.multiply.error", "Right: " << val);
+
+ throw_(value_error, _("Cannot multiply %1 with %2") << label() << 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:
+ as_amount_lval() /= val.as_amount();
+ return *this;
+ case BALANCE:
+ if (val.as_balance().single_amount()) {
+ value_t simpler(val.simplified());
+ switch (simpler.type()) {
+ case INTEGER:
+ as_amount_lval() /= simpler.as_long();
+ break;
+ case AMOUNT:
+ as_amount_lval() /= simpler.as_amount();
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ return *this;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case BALANCE:
+ switch (val.type()) {
+ case INTEGER:
+ as_balance_lval() /= val.as_long();
+ return *this;
+ case AMOUNT:
+ if (as_balance().single_amount()) {
+ in_place_simplify();
+ as_amount_lval() /= val.as_amount();
+ return *this;
+ }
+ else if (! val.as_amount().has_commodity()) {
+ as_balance_lval() /= val.as_amount();
+ return *this;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ throw_(value_error, _("Cannot divide %1 by %2") << label() << val.label());
+
+ return *this;
+}
+
+
+bool value_t::is_equal_to(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();
+ 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();
+ 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();
+ default:
+ break;
+ }
+ break;
+
+ case STRING:
+ if (val.is_string())
+ return as_string() == val.as_string();
+ break;
+
+ case MASK:
+ if (val.is_mask())
+ return as_mask() == val.as_mask();
+ break;
+
+ case SEQUENCE:
+ if (val.is_sequence())
+ return as_sequence() == val.as_sequence();
+ break;
+
+ default:
+ break;
+ }
+
+ throw_(value_error, _("Cannot compare %1 by %2") << label() << val.label());
+
+ return *this;
+}
+
+bool value_t::is_less_than(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:
+ try {
+ return as_amount() < val.as_amount();
+ }
+ catch (const amount_error&) {
+ return compare_amount_commodities()(&as_amount(), &val.as_amount());
+ }
+ default:
+ break;
+ }
+ break;
+
+ case BALANCE:
+ switch (val.type()) {
+ case INTEGER:
+ case AMOUNT: {
+ if (val.is_nonzero())
+ break;
+
+ bool no_amounts = true;
+ foreach (const balance_t::amounts_map::value_type& pair,
+ as_balance().amounts) {
+ if (pair.second >= 0L)
+ return false;
+ no_amounts = false;
+ }
+ return ! no_amounts;
+ }
+ default:
+ break;
+ }
+ break;
+
+ case STRING:
+ if (val.is_string())
+ return as_string() < val.as_string();
+ break;
+
+ case SEQUENCE:
+ switch (val.type()) {
+ case INTEGER:
+ case AMOUNT: {
+ if (val.is_nonzero())
+ break;
+
+ bool no_amounts = true;
+ foreach (const value_t& value, as_sequence()) {
+ if (value >= 0L)
+ return false;
+ no_amounts = false;
+ }
+ return ! no_amounts;
+ }
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ throw_(value_error, _("Cannot compare %1 to %2") << label() << val.label());
+
+ return *this;
+}
+
+bool value_t::is_greater_than(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 BALANCE:
+ switch (val.type()) {
+ case INTEGER:
+ case AMOUNT: {
+ if (val.is_nonzero())
+ break;
+
+ bool no_amounts = true;
+ foreach (const balance_t::amounts_map::value_type& pair,
+ as_balance().amounts) {
+ if (pair.second <= 0L)
+ return false;
+ no_amounts = false;
+ }
+ return ! no_amounts;
+ }
+ default:
+ break;
+ }
+ break;
+
+ case STRING:
+ if (val.is_string())
+ return as_string() > val.as_string();
+ break;
+
+ case SEQUENCE:
+ switch (val.type()) {
+ case INTEGER:
+ case AMOUNT: {
+ if (val.is_nonzero())
+ break;
+
+ bool no_amounts = true;
+ foreach (const value_t& value, as_sequence()) {
+ if (value <= 0L)
+ return false;
+ no_amounts = false;
+ }
+ return ! no_amounts;
+ }
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ throw_(value_error, _("Cannot compare %1 to %2") << label() << val.label());
+
+ return *this;
+}
+
+void value_t::in_place_cast(type_t cast_type)
+{
+ if (type() == cast_type)
+ return;
+
+ _dup();
+
+ 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 AMOUNT:
+ set_amount(as_boolean() ? 1L : 0L);
+ return;
+ case STRING:
+ set_string(as_boolean() ? "true" : "false");
+ return;
+ default:
+ break;
+ }
+ break;
+
+ case DATE:
+ switch (cast_type) {
+ case DATETIME:
+ set_datetime(datetime_t(as_date(), time_duration(0, 0, 0, 0)));
+ return;
+ case STRING:
+ set_string(format_date(as_date(), FMT_WRITTEN));
+ return;
+ default:
+ break;
+ }
+ break;
+ case DATETIME:
+ switch (cast_type) {
+ case DATE:
+ set_date(as_datetime().date());
+ return;
+ case STRING:
+ set_string(format_datetime(as_datetime(), FMT_WRITTEN));
+ return;
+ default:
+ break;
+ }
+ break;
+
+ case INTEGER:
+ switch (cast_type) {
+ case AMOUNT:
+ set_amount(as_long());
+ return;
+ case BALANCE:
+ set_balance(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());
+ return;
+ case STRING:
+ if (amt.is_null())
+ set_string("");
+ else
+ set_string(as_amount().to_string());
+ return;
+ default:
+ break;
+ }
+ break;
+ }
+
+ case BALANCE: {
+ const balance_t& bal(as_balance());
+ switch (cast_type) {
+ case AMOUNT: {
+ if (bal.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((*bal.amounts.begin()).second));
+ return;
+ }
+ else if (bal.amounts.size() == 0) {
+ set_amount(0L);
+ return;
+ }
+ else {
+ throw_(value_error, _("Cannot convert %1 with multiple commodities to %2")
+ << label() << label(cast_type));
+ }
+ break;
+ }
+ case STRING:
+ if (bal.is_empty())
+ set_string("");
+ else
+ set_string(as_balance().to_string());
+ 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;
+ }
+ break;
+ }
+ case AMOUNT:
+ set_amount(amount_t(as_string()));
+ return;
+ case DATE:
+ set_date(parse_date(as_string()));
+ return;
+ case DATETIME:
+ set_datetime(parse_datetime(as_string()));
+ return;
+ case MASK:
+ set_mask(as_string());
+ return;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ throw_(value_error,
+ _("Cannot convert %1 to %2") << label() << 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 SEQUENCE: {
+ value_t temp;
+ foreach (const value_t& value, as_sequence())
+ temp.push_back(- value);
+ *this = temp;
+ return;
+ }
+ default:
+ break;
+ }
+
+ throw_(value_error, _("Cannot negate %1") << label());
+}
+
+void value_t::in_place_not()
+{
+ switch (type()) {
+ case BOOLEAN:
+ set_boolean(! as_boolean());
+ return;
+ case INTEGER:
+ case DATETIME:
+ set_boolean(! as_long());
+ return;
+ case DATE:
+ set_boolean(! as_long());
+ return;
+ case AMOUNT:
+ set_boolean(! as_amount());
+ return;
+ case BALANCE:
+ set_boolean(! as_balance());
+ return;
+ case STRING:
+ set_boolean(as_string().empty());
+ return;
+ case SEQUENCE: {
+ value_t temp;
+ foreach (const value_t& value, as_sequence())
+ temp.push_back(! value);
+ *this = temp;
+ return;
+ }
+ default:
+ break;
+ }
+
+ throw_(value_error, _("Cannot 'not' %1") << 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 STRING:
+ return as_string().empty();
+ case SEQUENCE:
+ return as_sequence().empty();
+
+ case SCOPE:
+ return as_scope() == NULL;
+
+ default:
+ throw_(value_error, _("Cannot determine if %1 is really zero") << label());
+ }
+ return false;
+}
+
+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 STRING:
+ return as_string().empty();
+ case SEQUENCE:
+ return as_sequence().empty();
+
+ case SCOPE:
+ return as_scope() == NULL;
+
+ default:
+ throw_(value_error, _("Cannot determine if %1 is zero") << label());
+ }
+ return false;
+}
+
+value_t value_t::value(const bool primary_only,
+ const optional<datetime_t>& moment,
+ const optional<commodity_t&>& in_terms_of) const
+{
+ switch (type()) {
+ case INTEGER:
+ return NULL_VALUE;
+
+ case AMOUNT:
+ if (optional<amount_t> val =
+ as_amount().value(primary_only, moment, in_terms_of))
+ return *val;
+ return NULL_VALUE;
+
+ case BALANCE:
+ if (optional<balance_t> bal =
+ as_balance().value(primary_only, moment, in_terms_of))
+ return *bal;
+ return NULL_VALUE;
+
+ default:
+ break;
+ }
+
+ throw_(value_error, _("Cannot find the value of %1") << label());
+ return NULL_VALUE;
+}
+
+value_t value_t::price() const
+{
+ switch (type()) {
+ case AMOUNT:
+ return as_amount().price();
+ case BALANCE:
+ return as_balance().price();
+ default:
+ return *this;
+ }
+}
+
+value_t value_t::exchange_commodities(const std::string& commodities,
+ const bool add_prices,
+ const optional<datetime_t>& moment)
+{
+ scoped_array<char> buf(new char[commodities.length() + 1]);
+
+ std::strcpy(buf.get(), commodities.c_str());
+
+ for (char * p = std::strtok(buf.get(), ",");
+ p;
+ p = std::strtok(NULL, ",")) {
+ if (commodity_t * commodity =
+ commodity_pool_t::current_pool->parse_price_expression(p, add_prices,
+ moment)) {
+ value_t result = value(false, moment, *commodity);
+ if (! result.is_null())
+ return result;
+ }
+ }
+ return *this;
+}
+
+void value_t::in_place_reduce()
+{
+ switch (type()) {
+ case AMOUNT:
+ as_amount_lval().in_place_reduce();
+ return;
+ case BALANCE:
+ as_balance_lval().in_place_reduce();
+ return;
+ default:
+ return;
+ }
+
+ //throw_(value_error, "Cannot reduce " << label());
+}
+
+void value_t::in_place_unreduce()
+{
+ switch (type()) {
+ case AMOUNT:
+ as_amount_lval().in_place_unreduce();
+ return;
+ case BALANCE:
+ as_balance_lval().in_place_unreduce();
+ return;
+ default:
+ return;
+ }
+
+ //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();
+ default:
+ break;
+ }
+
+ throw_(value_error, _("Cannot abs %1") << label());
+ return NULL_VALUE;
+}
+
+void value_t::in_place_round()
+{
+ switch (type()) {
+ case INTEGER:
+ return;
+ case AMOUNT:
+ as_amount_lval().in_place_round();
+ return;
+ case BALANCE:
+ as_balance_lval().in_place_round();
+ return;
+ case SEQUENCE: {
+ value_t temp;
+ foreach (const value_t& value, as_sequence())
+ temp.push_back(value.rounded());
+ *this = temp;
+ return;
+ }
+ default:
+ break;
+ }
+
+ throw_(value_error, _("Cannot set rounding for %1") << label());
+}
+
+void value_t::in_place_truncate()
+{
+ switch (type()) {
+ case INTEGER:
+ return;
+ case AMOUNT:
+ as_amount_lval().in_place_truncate();
+ return;
+ case BALANCE:
+ as_balance_lval().in_place_truncate();
+ return;
+ case SEQUENCE: {
+ value_t temp;
+ foreach (const value_t& value, as_sequence())
+ temp.push_back(value.truncated());
+ *this = temp;
+ return;
+ }
+ default:
+ break;
+ }
+
+ throw_(value_error, _("Cannot truncate %1") << label());
+}
+
+void value_t::in_place_floor()
+{
+ switch (type()) {
+ case INTEGER:
+ return;
+ case AMOUNT:
+ as_amount_lval().in_place_floor();
+ return;
+ case BALANCE:
+ as_balance_lval().in_place_floor();
+ return;
+ case SEQUENCE: {
+ value_t temp;
+ foreach (const value_t& value, as_sequence())
+ temp.push_back(value.floored());
+ *this = temp;
+ return;
+ }
+ default:
+ break;
+ }
+
+ throw_(value_error, _("Cannot floor %1") << label());
+}
+
+void value_t::in_place_unround()
+{
+ switch (type()) {
+ case INTEGER:
+ return;
+ case AMOUNT:
+ as_amount_lval().in_place_unround();
+ return;
+ case BALANCE:
+ as_balance_lval().in_place_unround();
+ return;
+ case SEQUENCE: {
+ value_t temp;
+ foreach (const value_t& value, as_sequence())
+ temp.push_back(value.unrounded());
+ *this = temp;
+ return;
+ }
+ default:
+ break;
+ }
+
+ throw_(value_error, _("Cannot unround %1") << label());
+}
+
+void value_t::annotate(const annotation_t& details)
+{
+ if (is_amount())
+ as_amount_lval().annotate(details);
+ else
+ throw_(value_error, _("Cannot annotate %1") << label());
+}
+
+bool value_t::has_annotation() const
+{
+ if (is_amount())
+ return as_amount().has_annotation();
+ else
+ throw_(value_error,
+ _("Cannot determine whether %1 is annotated") << label());
+ return false;
+}
+
+annotation_t& value_t::annotation()
+{
+ if (is_amount())
+ return as_amount_lval().annotation();
+ else {
+ throw_(value_error, _("Cannot request annotation of %1") << label());
+ return as_amount_lval().annotation(); // quiet g++ warning
+ }
+}
+
+value_t value_t::strip_annotations(const keep_details_t& what_to_keep) const
+{
+ if (what_to_keep.keep_all())
+ return *this;
+
+ switch (type()) {
+ case VOID:
+ case BOOLEAN:
+ case INTEGER:
+ case DATETIME:
+ case DATE:
+ case STRING:
+ case MASK:
+ case SCOPE:
+ return *this;
+
+ case SEQUENCE: {
+ sequence_t temp;
+ foreach (const value_t& value, as_sequence())
+ temp.push_back(value.strip_annotations(what_to_keep));
+ return temp;
+ }
+
+ case AMOUNT:
+ return as_amount().strip_annotations(what_to_keep);
+ case BALANCE:
+ return as_balance().strip_annotations(what_to_keep);
+
+ default:
+ assert(false);
+ break;
+ }
+ assert(false);
+ return NULL_VALUE;
+}
+
+void value_t::print(std::ostream& out,
+ const int first_width,
+ const int latter_width,
+ const bool right_justify,
+ const bool colorize) const
+{
+ if (first_width > 0 &&
+ (! is_amount() || as_amount().is_zero()) &&
+ ! is_balance() && ! is_string()) {
+ out.width(first_width);
+
+ if (right_justify)
+ out << std::right;
+ else
+ out << std::left;
+ }
+
+ switch (type()) {
+ case VOID:
+ out << "";
+ break;
+
+ case BOOLEAN:
+ out << (as_boolean() ? "1" : "0");
+ break;
+
+ case DATETIME:
+ out << format_datetime(as_datetime(), FMT_WRITTEN);
+ break;
+
+ case DATE:
+ out << format_date(as_date(), FMT_WRITTEN);
+ break;
+
+ case INTEGER:
+ if (colorize && as_long() < 0)
+ justify(out, to_string(), first_width, right_justify, true);
+ else
+ out << as_long();
+ break;
+
+ case AMOUNT: {
+ if (as_amount().is_zero()) {
+ out << 0;
+ } else {
+ std::ostringstream buf;
+ buf << as_amount();
+ justify(out, buf.str(), first_width, right_justify,
+ colorize && as_amount().sign() < 0);
+ }
+ break;
+ }
+
+ case BALANCE:
+ as_balance().print(out, first_width, latter_width, right_justify,
+ colorize);
+ break;
+
+ case STRING:
+ if (first_width > 0)
+ justify(out, as_string(), first_width, right_justify);
+ else
+ out << as_string();
+ break;
+
+ case MASK:
+ out << '/' << as_mask() << '/';
+ 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, right_justify,
+ colorize);
+ }
+ out << ')';
+ break;
+ }
+
+ case SCOPE:
+ out << "<SCOPE>";
+ break;
+
+ default:
+ throw_(value_error, _("Cannot print %1") << label());
+ }
+}
+
+void value_t::dump(std::ostream& out, const bool relaxed) const
+{
+ switch (type()) {
+ case VOID:
+ out << "null";
+ break;
+
+ case BOOLEAN:
+ if (as_boolean())
+ out << "true";
+ else
+ out << "false";
+ break;
+
+ case DATETIME:
+ out << '[' << format_datetime(as_datetime(), FMT_WRITTEN) << ']';
+ break;
+ case DATE:
+ out << '[' << format_date(as_date(), FMT_WRITTEN) << ']';
+ break;
+
+ case INTEGER:
+ out << as_long();
+ break;
+
+ case AMOUNT:
+ if (! relaxed)
+ out << '{';
+ out << as_amount();
+ if (! relaxed)
+ out << '}';
+ break;
+
+ case BALANCE:
+ out << as_balance();
+ break;
+
+ case STRING:
+ out << '"';
+ foreach (const char& ch, as_string()) {
+ switch (ch) {
+ case '"':
+ out << "\\\"";
+ break;
+ case '\\':
+ out << "\\\\";
+ break;
+ default:
+ out << ch;
+ break;
+ }
+ }
+ out << '"';
+ break;
+
+ case MASK:
+ out << '/' << as_mask() << '/';
+ break;
+
+ case SCOPE:
+ out << as_scope();
+ break;
+
+ case SEQUENCE: {
+ out << '(';
+ bool first = true;
+ foreach (const value_t& value, as_sequence()) {
+ if (first)
+ first = false;
+ else
+ out << ", ";
+
+ value.dump(out, relaxed);
+ }
+ out << ')';
+ break;
+ }
+
+ default:
+ assert(false);
+ break;
+ }
+}
+
+bool value_t::valid() const
+{
+ switch (type()) {
+ case AMOUNT:
+ return as_amount().valid();
+ case BALANCE:
+ return as_balance().valid();
+ default:
+ break;
+ }
+ return true;
+}
+
+bool sort_value_is_less_than(const std::list<sort_value_t>& left_values,
+ const std::list<sort_value_t>& right_values)
+{
+ std::list<sort_value_t>::const_iterator left_iter = left_values.begin();
+ std::list<sort_value_t>::const_iterator right_iter = right_values.begin();
+
+ while (left_iter != left_values.end() && right_iter != right_values.end()) {
+ // Don't even try to sort balance values
+ if (! (*left_iter).value.is_balance() &&
+ ! (*right_iter).value.is_balance()) {
+ DEBUG("value.sort",
+ " Comparing " << (*left_iter).value << " < " << (*right_iter).value);
+ if ((*left_iter).value < (*right_iter).value) {
+ DEBUG("value.sort", " is less");
+ return ! (*left_iter).inverted;
+ }
+ else if ((*left_iter).value > (*right_iter).value) {
+ DEBUG("value.sort", " is greater");
+ return (*left_iter).inverted;
+ }
+ }
+ left_iter++; right_iter++;
+ }
+
+ assert(left_iter == left_values.end());
+ assert(right_iter == right_values.end());
+
+ return false;
+}
+
+void to_xml(std::ostream& out, const value_t& value)
+{
+ switch (value.type()) {
+ case value_t::VOID:
+ out << "<void />";
+ break;
+ case value_t::BOOLEAN: {
+ push_xml y(out, "boolean");
+ out << (value.as_boolean() ? "true" : "false");
+ break;
+ }
+ case value_t::INTEGER: {
+ push_xml y(out, "integer");
+ out << value.as_long();
+ break;
+ }
+
+ case value_t::AMOUNT:
+ to_xml(out, value.as_amount());
+ break;
+ case value_t::BALANCE:
+ to_xml(out, value.as_balance());
+ break;
+
+ case value_t::DATETIME:
+ to_xml(out, value.as_datetime());
+ break;
+ case value_t::DATE:
+ to_xml(out, value.as_date());
+ break;
+ case value_t::STRING: {
+ push_xml y(out, "string");
+ out << y.guard(value.as_string());
+ break;
+ }
+ case value_t::MASK:
+ to_xml(out, value.as_mask());
+ break;
+
+ case value_t::SEQUENCE: {
+ push_xml y(out, "sequence");
+ foreach (const value_t& member, value.as_sequence())
+ to_xml(out, member);
+ break;
+ }
+
+ case value_t::SCOPE:
+ default:
+ assert(false);
+ break;
+ }
+}
+
+} // namespace ledger