summaryrefslogtreecommitdiff
path: root/src/pool.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/pool.cc')
-rw-r--r--src/pool.cc364
1 files changed, 364 insertions, 0 deletions
diff --git a/src/pool.cc b/src/pool.cc
new file mode 100644
index 00000000..70f4eed6
--- /dev/null
+++ b/src/pool.cc
@@ -0,0 +1,364 @@
+/*
+ * 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 "amount.h"
+#include "commodity.h"
+#include "annotate.h"
+#include "pool.h"
+#include "quotes.h"
+
+namespace ledger {
+
+shared_ptr<commodity_pool_t> commodity_pool_t::current_pool;
+
+commodity_pool_t::commodity_pool_t()
+ : default_commodity(NULL), keep_base(false),
+ quote_leeway(86400), get_quotes(false),
+ get_commodity_quote(commodity_quote_from_script)
+{
+ TRACE_CTOR(commodity_pool_t, "");
+ null_commodity = create("");
+ null_commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET);
+}
+
+commodity_t * commodity_pool_t::create(const string& symbol)
+{
+ shared_ptr<commodity_t::base_t>
+ base_commodity(new commodity_t::base_t(symbol));
+ std::auto_ptr<commodity_t> commodity(new commodity_t(this, base_commodity));
+
+ DEBUG("amounts.commodities", "Creating base commodity " << symbol);
+
+ // Create the "qualified symbol" version of this commodity's symbol
+ if (commodity_t::symbol_needs_quotes(symbol)) {
+ commodity->qualified_symbol = "\"";
+ *commodity->qualified_symbol += symbol;
+ *commodity->qualified_symbol += "\"";
+ }
+
+ DEBUG("amounts.commodities",
+ "Creating commodity '" << commodity->symbol() << "'");
+
+ std::pair<commodities_map::iterator, bool> result
+ = commodities.insert(commodities_map::value_type(commodity->mapping_key(),
+ commodity.get()));
+ assert(result.second);
+
+ return commodity.release();
+}
+
+commodity_t * commodity_pool_t::find_or_create(const string& symbol)
+{
+ DEBUG("amounts.commodities", "Find-or-create commodity " << symbol);
+
+ commodity_t * commodity = find(symbol);
+ if (commodity)
+ return commodity;
+ return create(symbol);
+}
+
+commodity_t * commodity_pool_t::find(const string& symbol)
+{
+ DEBUG("amounts.commodities", "Find commodity " << symbol);
+
+ commodities_map::const_iterator i = commodities.find(symbol);
+ if (i != commodities.end())
+ return (*i).second;
+ return NULL;
+}
+
+commodity_t *
+commodity_pool_t::create(const string& symbol, const annotation_t& details)
+{
+ commodity_t * new_comm = create(symbol);
+ if (! new_comm)
+ return NULL;
+
+ if (details)
+ return find_or_create(*new_comm, details);
+ else
+ return new_comm;
+}
+
+string commodity_pool_t::make_qualified_name(const commodity_t& comm,
+ const annotation_t& details)
+{
+ assert(details);
+
+ if (details.price && details.price->sign() < 0)
+ throw_(amount_error, _("A commodity's price may not be negative"));
+
+ std::ostringstream name;
+ comm.print(name);
+ details.print(name, comm.pool().keep_base);
+
+ DEBUG("amounts.commodities", "make_qualified_name for "
+ << *comm.qualified_symbol << std::endl << details);
+ DEBUG("amounts.commodities", "qualified_name is " << name.str());
+
+ return name.str();
+}
+
+commodity_t *
+commodity_pool_t::find(const string& symbol, const annotation_t& details)
+{
+ commodity_t * comm = find(symbol);
+ if (! comm)
+ return NULL;
+
+ if (details) {
+ string name = make_qualified_name(*comm, details);
+
+ if (commodity_t * ann_comm = find(name)) {
+ assert(ann_comm->annotated && as_annotated_commodity(*ann_comm).details);
+ return ann_comm;
+ }
+ return NULL;
+ } else {
+ return comm;
+ }
+}
+
+commodity_t *
+commodity_pool_t::find_or_create(const string& symbol,
+ const annotation_t& details)
+{
+ commodity_t * comm = find(symbol);
+ if (! comm)
+ return NULL;
+
+ if (details)
+ return find_or_create(*comm, details);
+ else
+ return comm;
+}
+
+commodity_t *
+commodity_pool_t::create(commodity_t& comm,
+ const annotation_t& details,
+ const string& mapping_key)
+{
+ assert(comm);
+ assert(details);
+ assert(! mapping_key.empty());
+
+ std::auto_ptr<commodity_t> commodity
+ (new annotated_commodity_t(&comm, details));
+
+ commodity->qualified_symbol = comm.symbol();
+ assert(! commodity->qualified_symbol->empty());
+
+ DEBUG("amounts.commodities", "Creating annotated commodity "
+ << "symbol " << commodity->symbol()
+ << " key " << mapping_key << std::endl << details);
+
+ // Add the fully annotated name to the map, so that this symbol may
+ // quickly be found again.
+ commodity->mapping_key_ = mapping_key;
+
+ std::pair<commodities_map::iterator, bool> result
+ = commodities.insert(commodities_map::value_type(mapping_key,
+ commodity.get()));
+ assert(result.second);
+
+ return commodity.release();
+}
+
+commodity_t * commodity_pool_t::find_or_create(commodity_t& comm,
+ const annotation_t& details)
+{
+ assert(comm);
+ assert(details);
+
+ string name = make_qualified_name(comm, details);
+ assert(! name.empty());
+
+ if (commodity_t * ann_comm = find(name)) {
+ assert(ann_comm->annotated && as_annotated_commodity(*ann_comm).details);
+ return ann_comm;
+ }
+ return create(comm, details, name);
+}
+
+void commodity_pool_t::exchange(commodity_t& commodity,
+ const amount_t& per_unit_cost,
+ const datetime_t& moment)
+{
+ DEBUG("commodity.prices.add", "exchanging commodity " << commodity
+ << " at per unit cost " << per_unit_cost << " on " << moment);
+
+ commodity_t& base_commodity
+ (commodity.annotated ?
+ as_annotated_commodity(commodity).referent() : commodity);
+
+ base_commodity.add_price(moment, per_unit_cost);
+}
+
+cost_breakdown_t
+commodity_pool_t::exchange(const amount_t& amount,
+ const amount_t& cost,
+ const bool is_per_unit,
+ const optional<datetime_t>& moment,
+ const optional<string>& tag)
+{
+ DEBUG("commodity.prices.add", "exchange: " << amount << " for " << cost);
+ DEBUG("commodity.prices.add", "exchange: is-per-unit = " << is_per_unit);
+#if defined(DEBUG_ON)
+ if (moment)
+ DEBUG("commodity.prices.add", "exchange: moment = " << *moment);
+ if (tag)
+ DEBUG("commodity.prices.add", "exchange: tag = " << *tag);
+#endif
+
+ commodity_t& commodity(amount.commodity());
+
+ annotation_t * current_annotation = NULL;
+ if (commodity.annotated)
+ current_annotation = &as_annotated_commodity(commodity).details;
+
+ amount_t per_unit_cost =
+ (is_per_unit || amount.is_realzero() ? cost : cost / amount).abs();
+
+ DEBUG("commodity.prices.add", "exchange: per-unit-cost = " << per_unit_cost);
+
+ if (! per_unit_cost.is_realzero())
+ exchange(commodity, per_unit_cost, moment ? *moment : CURRENT_TIME());
+
+ cost_breakdown_t breakdown;
+ breakdown.final_cost = ! is_per_unit ? cost : cost * amount;
+
+ DEBUG("commodity.prices.add",
+ "exchange: final-cost = " << breakdown.final_cost);
+
+ if (current_annotation && current_annotation->price)
+ breakdown.basis_cost
+ = (*current_annotation->price * amount).unrounded();
+ else
+ breakdown.basis_cost = breakdown.final_cost;
+
+ DEBUG("commodity.prices.add",
+ "exchange: basis-cost = " << breakdown.basis_cost);
+
+ annotation_t annotation(per_unit_cost, moment ?
+ moment->date() : optional<date_t>(), tag);
+
+ annotation.add_flags(ANNOTATION_PRICE_CALCULATED);
+ if (moment)
+ annotation.add_flags(ANNOTATION_DATE_CALCULATED);
+ if (tag)
+ annotation.add_flags(ANNOTATION_TAG_CALCULATED);
+
+ breakdown.amount = amount_t(amount, annotation);
+
+ DEBUG("commodity.prices.add",
+ "exchange: amount = " << breakdown.amount);
+
+ return breakdown;
+}
+
+optional<std::pair<commodity_t *, price_point_t> >
+commodity_pool_t::parse_price_directive(char * line, bool do_not_add_price)
+{
+ char * date_field_ptr = line;
+ char * time_field_ptr = next_element(date_field_ptr);
+ if (! time_field_ptr) return none;
+ string date_field = date_field_ptr;
+
+ char * symbol_and_price;
+ datetime_t datetime;
+ string symbol;
+
+ if (std::isdigit(time_field_ptr[0])) {
+ symbol_and_price = next_element(time_field_ptr);
+ if (! symbol_and_price) return none;
+
+ datetime = parse_datetime(date_field + " " + time_field_ptr);
+ }
+ else if (std::isdigit(date_field_ptr[0])) {
+ symbol_and_price = time_field_ptr;
+ datetime = datetime_t(parse_date(date_field));
+ }
+ else {
+ symbol = date_field_ptr;
+ symbol_and_price = time_field_ptr;
+ datetime = CURRENT_TIME();
+ }
+
+ if (symbol.empty())
+ commodity_t::parse_symbol(symbol_and_price, symbol);
+
+ price_point_t point;
+ point.when = datetime;
+ point.price.parse(symbol_and_price, PARSE_NO_MIGRATE);
+ VERIFY(point.price.valid());
+
+ DEBUG("commodity.download", "Looking up symbol: " << symbol);
+ if (commodity_t * commodity = find_or_create(symbol)) {
+ DEBUG("commodity.download", "Adding price for " << symbol << ": "
+ << point.when << " " << point.price);
+ if (! do_not_add_price)
+ commodity->add_price(point.when, point.price, true);
+ commodity->add_flags(COMMODITY_KNOWN);
+ return std::pair<commodity_t *, price_point_t>(commodity, point);
+ }
+
+ return none;
+}
+
+commodity_t *
+commodity_pool_t::parse_price_expression(const std::string& str,
+ const bool add_prices,
+ const optional<datetime_t>& moment)
+{
+ scoped_array<char> buf(new char[str.length() + 1]);
+
+ std::strcpy(buf.get(), str.c_str());
+
+ char * price = std::strchr(buf.get(), '=');
+ if (price)
+ *price++ = '\0';
+
+ if (commodity_t * commodity = find_or_create(trim_ws(buf.get()))) {
+ if (price && add_prices) {
+ for (char * p = std::strtok(price, ";");
+ p;
+ p = std::strtok(NULL, ";")) {
+ commodity->add_price(moment ? *moment : CURRENT_TIME(), amount_t(p));
+ }
+ }
+ return commodity;
+ }
+ return NULL;
+}
+
+} // namespace ledger