gnucash/libgnucash/engine/kvp-value.cpp
Maarten Bosmans 35aeed45ec [engine] Remove two replace functions from KvpValue
These were not used outside a test.

And that test was not leak free, as a result of the functions not doing
what they are supposed to do when the current value is not of the type
that is expected. (NULL is returned, but the value is not replaced)
2023-04-02 21:12:50 +02:00

387 lines
10 KiB
C++

/********************************************************************\
* 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 <cmath>
#include <sstream>
#include <iomanip>
#include <stdexcept>
using boost::typeindex::type_id;
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() == type_id<GList*>())
{
GList * list = boost::get<GList*>(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() == type_id<int64_t>())
return KvpValue::Type::INT64;
else if (datastore.type() == type_id<double>())
return KvpValue::Type::DOUBLE;
else if (datastore.type() == type_id<gnc_numeric>())
return KvpValue::Type::NUMERIC;
else if (datastore.type() == type_id<const gchar *>())
return KvpValue::Type::STRING;
else if (datastore.type() == type_id<GncGUID *>())
return KvpValue::Type::GUID;
else if (datastore.type() == type_id<Time64>())
return KvpValue::Type::TIME64;
else if (datastore.type() == type_id<GList *>())
return KvpValue::Type::GLIST;
else if (datastore.type() == type_id<KvpFrameImpl *>())
return KvpValue::Type::FRAME;
else if (datastore.type() == type_id<GDate>())
return KvpValue::Type::GDATE;
return KvpValue::Type::INVALID;
}
struct to_string_visitor : boost::static_visitor<void>
{
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<const KvpValue *>(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() == type_id<KvpFrame*>())
return this->get<KvpFrame*>()->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<KvpValue *>(lptr->data));
}
return retval;
}
struct compare_visitor : boost::static_visitor<int>
{
template <typename T, typename U>
int operator()( T& one, U& two) const
{
throw std::invalid_argument{"You may not compare objects of different type."};
}
template <typename T>
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<void>
{
template <typename T> void
operator()(T &) { /*do nothing*/ }
};
static void
destroy_value(void* item)
{
delete static_cast<KvpValue*>(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<gchar *> (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() == type_id<const gchar *>())
this->datastore = const_cast<const gchar *>(g_strdup(other.get<const gchar *>()));
else if (other.datastore.type() == type_id<GncGUID*>())
this->datastore = guid_copy(other.get<GncGUID *>());
else if (other.datastore.type() == type_id<GList*>())
this->datastore = kvp_glist_copy(other.get<GList *>());
else if (other.datastore.type() == type_id<KvpFrame*>())
this->datastore = new KvpFrame(*other.get<KvpFrame *>());
else
this->datastore = other.datastore;
}