/********************************************************************\ * kvp-value.cpp -- Implements a key-value frame system * * Copyright (C) 2014 Aaron Laws * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License* * along with this program; if not, contact: * * * * Free Software Foundation Voice: +1-617-542-5942 * * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * * Boston, MA 02110-1301, USA gnu@gnu.org * * * \********************************************************************/ #include "kvp-value.hpp" #include "kvp-frame.hpp" #include #include #include #include KvpValueImpl::KvpValueImpl(KvpValueImpl const & other) noexcept { duplicate(other); } KvpValueImpl& KvpValueImpl::operator=(KvpValueImpl const & other) noexcept { duplicate(other); return *this; } KvpValueImpl::KvpValueImpl(KvpValueImpl && b) noexcept { datastore = b.datastore; b.datastore = INT64_C(0); } KvpValueImpl& KvpValueImpl::operator=(KvpValueImpl && b) noexcept { std::swap (datastore, b.datastore); return *this; } KvpValueImpl * KvpValueImpl::add(KvpValueImpl * val) noexcept { /* If already a glist here, just append */ if (this->datastore.type() == typeid(GList*)) { GList * list = boost::get(datastore); datastore = g_list_append (list, val); return this; } /* If some other value, convert it to a glist */ GList *list = nullptr; list = g_list_append (list, this); list = g_list_append (list, val); return new KvpValueImpl(list); } KvpValue::Type KvpValueImpl::get_type() const noexcept { if (datastore.type() == typeid(int64_t)) return KvpValue::Type::INT64; else if (datastore.type() == typeid(double)) return KvpValue::Type::DOUBLE; else if (datastore.type() == typeid(gnc_numeric)) return KvpValue::Type::NUMERIC; else if (datastore.type() == typeid(const gchar *)) return KvpValue::Type::STRING; else if (datastore.type() == typeid(GncGUID *)) return KvpValue::Type::GUID; else if (datastore.type() == typeid(Time64)) return KvpValue::Type::TIME64; else if (datastore.type() == typeid(GList *)) return KvpValue::Type::GLIST; else if (datastore.type() == typeid(KvpFrameImpl *)) return KvpValue::Type::FRAME; else if (datastore.type() == typeid(GDate)) return KvpValue::Type::GDATE; return KvpValue::Type::INVALID; } KvpFrame * KvpValueImpl::replace_frame_nc (KvpFrame * new_value) noexcept { if (datastore.type() != typeid(KvpFrame *)) return {}; auto ret = boost::get(datastore); datastore = new_value; return ret; } GList * KvpValueImpl::replace_glist_nc (GList * new_value) noexcept { if (datastore.type() != typeid(GList *)) return {}; auto ret = boost::get(datastore); datastore = new_value; return ret; } struct to_string_visitor : boost::static_visitor { std::ostringstream & output; to_string_visitor(std::ostringstream & val) : output(val){} void operator()(int64_t val) { output << val << " (64-bit int)"; } void operator()(KvpFrame* val) { output << val->to_string(); } void operator()(GDate val) { output << std::setw(4) << g_date_get_year(&val) << '-'; output << std::setw(2) << g_date_get_month(&val) << '-'; output << std::setw(2) << g_date_get_day(&val); output << " (gdate)"; } void operator()(GList * val) { output << "KVP_VALUE_GLIST("; output << "[ "; /*Since val is passed by value, we can modify it*/ for (;val; val = val->next) { auto realvalue = static_cast(val->data); output << ' ' << realvalue->to_string() << ','; } output << " ]"; output << ")"; } void operator()(Time64 val) { char tmp1[MAX_DATE_LENGTH + 1] {}; gnc_time64_to_iso8601_buff (val.t, tmp1); output << tmp1 << " (time64)"; } void operator()(gnc_numeric val) { auto tmp1 = gnc_numeric_to_string(val); if (tmp1) { output << tmp1; g_free(tmp1); } else { output << "(null)"; } output << " (gnc_numeric)"; } void operator()(GncGUID * val) { char guidstr[GUID_ENCODING_LENGTH+1]; if (val) { guid_to_string_buff(val,guidstr); output << guidstr; } else { output << "(null)"; } output << " (guid)"; } void operator()(const char * val) { output << val << " (char *)"; } void operator()(double val) { output << val << " (double)"; } }; std::string KvpValueImpl::to_string(std::string const & prefix) const noexcept { if (this->datastore.type() == typeid(KvpFrame*)) return this->get()->to_string(prefix); std::ostringstream ret; to_string_visitor visitor {ret}; boost::apply_visitor(visitor, datastore); /*We still use g_strdup since the return value will be freed by g_free*/ return prefix + ret.str(); } std::string KvpValueImpl::to_string() const noexcept { return to_string(""); } static int kvp_glist_compare(const GList * list1, const GList * list2) { const GList *lp1; const GList *lp2; if (list1 == list2) return 0; /* Nothing is always less than something */ if (!list1 && list2) return -1; if (list1 && !list2) return 1; lp1 = list1; lp2 = list2; while (lp1 && lp2) { KvpValue *v1 = (KvpValue *) lp1->data; KvpValue *v2 = (KvpValue *) lp2->data; gint vcmp = compare(v1, v2); if (vcmp != 0) return vcmp; lp1 = lp1->next; lp2 = lp2->next; } if (!lp1 && lp2) return -1; if (!lp2 && lp1) return 1; return 0; } static GList * kvp_glist_copy(const GList * list) { GList * retval = NULL; GList * lptr; if (!list) return retval; /* Duplicate the backbone of the list (this duplicates the POINTERS * to the values; we need to deep-copy the values separately) */ retval = g_list_copy((GList *) list); /* This step deep-copies the values */ for (lptr = retval; lptr; lptr = lptr->next) { lptr->data = new KvpValue(*static_cast(lptr->data)); } return retval; } struct compare_visitor : boost::static_visitor { template int operator()( T& one, U& two) const { throw std::invalid_argument{"You may not compare objects of different type."}; } template int operator()( T & one, T & two) const { /*This will work for any type that implements < and ==.*/ if (one < two) return -1; if (two < one) return 1; return 0; } }; template <> int compare_visitor::operator()(const char * const & one, const char * const & two) const { return strcmp(one, two); } template <> int compare_visitor::operator()(gnc_numeric const & one, gnc_numeric const & two) const { return gnc_numeric_compare(one, two); } template <> int compare_visitor::operator()(GncGUID * const & one, GncGUID * const & two) const { return guid_compare(one, two); } template <> int compare_visitor::operator()(Time64 const & one, Time64 const & two) const { return one.t < two.t ? -1 : one.t > two.t ? 1 : 0; } template <> int compare_visitor::operator()(GDate const & one, GDate const & two) const { return g_date_compare(&one,&two); } template <> int compare_visitor::operator()(GList * const & one, GList * const & two) const { return kvp_glist_compare(one, two); } template <> int compare_visitor::operator()(KvpFrame * const & one, KvpFrame * const & two) const { return compare(one, two); } template <> int compare_visitor::operator()(double const & one, double const & two) const { if (std::isnan(one) && std::isnan(two)) return 0; if (one < two) return -1; if (two < one) return 1; return 0; } int compare(const KvpValueImpl & one, const KvpValueImpl & two) noexcept { auto type1 = one.get_type(); auto type2 = two.get_type(); if (type1 != type2) return type1 < type2 ? -1 : 1; compare_visitor comparer; return boost::apply_visitor(comparer, one.datastore, two.datastore); } int compare(const KvpValueImpl * one, const KvpValueImpl * two) noexcept { if (one == two) return 0; if (one && !two) return 1; if (!one && two) return -1; assert (one && two); /* Silence a static analysis warning. */ return compare(*one, *two); } struct delete_visitor : boost::static_visitor { template void operator()(T &) { /*do nothing*/ } }; static void destroy_value(void* item) { delete static_cast(item); } template <> void delete_visitor::operator()(GList * & value) { g_list_free_full(value, destroy_value); } template <> void delete_visitor::operator()(const gchar * & value) { g_free(const_cast (value)); } template <> void delete_visitor::operator()(GncGUID * & value) { guid_free(value); } template <> void delete_visitor::operator()(KvpFrame * & value) { delete value; } KvpValueImpl::~KvpValueImpl() noexcept { delete_visitor d; boost::apply_visitor(d, datastore); } void KvpValueImpl::duplicate(const KvpValueImpl& other) noexcept { if (other.datastore.type() == typeid(const gchar *)) this->datastore = const_cast(g_strdup(other.get())); else if (other.datastore.type() == typeid(GncGUID*)) this->datastore = guid_copy(other.get()); else if (other.datastore.type() == typeid(GList*)) this->datastore = kvp_glist_copy(other.get()); else if (other.datastore.type() == typeid(KvpFrame*)) this->datastore = new KvpFrame(*other.get()); else this->datastore = other.datastore; }