mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Restructure the src directory
It is split into - /libgnucash (for the non-gui bits) - /gnucash (for the gui) - /common (misc source files used by both) - /bindings (currently only holds python bindings) This is the first step in restructuring the code. It will need much more fine tuning later on.
This commit is contained in:
400
libgnucash/engine/kvp-value.cpp
Normal file
400
libgnucash/engine/kvp-value.cpp
Normal file
@@ -0,0 +1,400 @@
|
||||
/********************************************************************\
|
||||
* 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>
|
||||
|
||||
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<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() == 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(Timespec))
|
||||
return KvpValue::Type::TIMESPEC;
|
||||
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<KvpFrame *>(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<GList *>(datastore);
|
||||
datastore = new_value;
|
||||
return ret;
|
||||
}
|
||||
|
||||
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 << "KVP_VALUE_GINT64(" << val << ")";
|
||||
}
|
||||
|
||||
void operator()(KvpFrame * val)
|
||||
{
|
||||
output << "KVP_VALUE_FRAME(" << val->to_string() << ")";
|
||||
}
|
||||
|
||||
void operator()(GDate val)
|
||||
{
|
||||
output << "KVP_VALUE_GDATE(";
|
||||
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) << ')';
|
||||
}
|
||||
|
||||
void operator()(GList * val)
|
||||
{
|
||||
output << "KVP_VALUE_GLIST(";
|
||||
output << "[ ";
|
||||
|
||||
/*Since val is passed by value, we can modify it*/
|
||||
for (;val; val = val->next)
|
||||
{
|
||||
gchar *tmp3;
|
||||
auto realvalue = static_cast<const KvpValue *>(val->data);
|
||||
tmp3 = realvalue->to_string();
|
||||
output << ' ';
|
||||
if (tmp3)
|
||||
{
|
||||
output << tmp3;
|
||||
g_free(tmp3);
|
||||
}
|
||||
output << ',';
|
||||
}
|
||||
|
||||
output << " ]";
|
||||
output << ")";
|
||||
}
|
||||
|
||||
void operator()(Timespec val)
|
||||
{
|
||||
char tmp1[40] {};
|
||||
gnc_timespec_to_iso8601_buff (val, tmp1);
|
||||
output << "KVP_VALUE_TIMESPEC(" << tmp1 << ")";
|
||||
}
|
||||
|
||||
void operator()(gnc_numeric val)
|
||||
{
|
||||
auto tmp1 = gnc_numeric_to_string(val);
|
||||
output << "KVP_VALUE_NUMERIC(";
|
||||
if (tmp1)
|
||||
{
|
||||
output << tmp1;
|
||||
g_free(tmp1);
|
||||
}
|
||||
output << ")";
|
||||
}
|
||||
|
||||
void operator()(GncGUID * val)
|
||||
{
|
||||
char guidstr[GUID_ENCODING_LENGTH+1];
|
||||
output << "KVP_VALUE_GUID(";
|
||||
if (val)
|
||||
{
|
||||
guid_to_string_buff(val,guidstr);
|
||||
output << guidstr;
|
||||
}
|
||||
output << ")";
|
||||
}
|
||||
|
||||
void operator()(const char * val)
|
||||
{
|
||||
output << "KVP_VALUE_STRING(" << val << ")";
|
||||
}
|
||||
|
||||
void operator()(double val)
|
||||
{
|
||||
output << "KVP_VALUE_DOUBLE(" << val << ")";
|
||||
}
|
||||
};
|
||||
|
||||
char *
|
||||
KvpValueImpl::to_string() const noexcept
|
||||
{
|
||||
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 g_strdup(ret.str().c_str());
|
||||
}
|
||||
|
||||
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()(Timespec const & one, Timespec const & two) const
|
||||
{
|
||||
return timespec_cmp(&one,&two);
|
||||
}
|
||||
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;
|
||||
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()(gchar * & value)
|
||||
{
|
||||
g_free(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 = g_strdup(other.get<const gchar *>());
|
||||
else if (other.datastore.type() == typeid(GncGUID*))
|
||||
this->datastore = guid_copy(other.get<GncGUID *>());
|
||||
else if (other.datastore.type() == typeid(GList*))
|
||||
this->datastore = kvp_glist_copy(other.get<GList *>());
|
||||
else if (other.datastore.type() == typeid(KvpFrame*))
|
||||
this->datastore = new KvpFrame(*other.get<KvpFrame *>());
|
||||
else
|
||||
this->datastore = other.datastore;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user