mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Implement KvpFrame in C++ using std::vector
KvpFrame was implemented using GList. Given the current desire to distance ourselves from glib and acquaint the project with C++, the standard library thereof, and boost libraries, KvpFrame has been replaced by an implementation that uses a std::map< const char *, KvpValueImpl *>. There were some cases of the KvpFrame's glist being accessed directly. A new API to help callers access the KvpFrame's contents systematically by providing a list of keys has been created, and call sites of the GList code have been updated. Another deprecated #define was found and removed (kvp_frame_set_str).
This commit is contained in:
parent
6c2a42bf49
commit
076f1fb25d
@ -258,10 +258,8 @@ add_text_to_node(xmlNodePtr node, gchar *type, gchar *val)
|
||||
g_free(newval);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
add_kvp_slot(gpointer key, gpointer value, gpointer data);
|
||||
add_kvp_slot(const char * key, KvpValue* value, xmlNodePtr node);
|
||||
|
||||
static void
|
||||
add_kvp_value_node(xmlNodePtr node, gchar *tag, KvpValue* val)
|
||||
@ -342,15 +340,19 @@ add_kvp_value_node(xmlNodePtr node, gchar *tag, KvpValue* val)
|
||||
case KVP_TYPE_FRAME:
|
||||
{
|
||||
KvpFrame *frame;
|
||||
const char ** keys;
|
||||
unsigned int i;
|
||||
|
||||
xmlSetProp(val_node, BAD_CAST "type", BAD_CAST "frame");
|
||||
|
||||
frame = kvp_value_get_frame (val);
|
||||
if (!frame || !kvp_frame_get_hash (frame))
|
||||
if (!frame)
|
||||
break;
|
||||
|
||||
g_hash_table_foreach_sorted(kvp_frame_get_hash(frame),
|
||||
add_kvp_slot, val_node, (GCompareFunc)strcmp);
|
||||
keys = kvp_frame_get_keys(frame);
|
||||
for (i = 0; keys[i]; ++i)
|
||||
add_kvp_slot(keys[i], kvp_frame_get_value(frame, keys[i]), val_node);
|
||||
g_free(keys);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -359,43 +361,36 @@ add_kvp_value_node(xmlNodePtr node, gchar *tag, KvpValue* val)
|
||||
}
|
||||
|
||||
static void
|
||||
add_kvp_slot(gpointer key, gpointer value, gpointer data)
|
||||
add_kvp_slot(const char * key, KvpValue* value, xmlNodePtr node)
|
||||
{
|
||||
xmlNodePtr slot_node;
|
||||
xmlNodePtr node = (xmlNodePtr)data;
|
||||
gchar *newkey = g_strdup ((gchar*)key);
|
||||
slot_node = xmlNewChild(node, NULL, BAD_CAST "slot", NULL);
|
||||
|
||||
xmlNewTextChild(slot_node, NULL, BAD_CAST "slot:key",
|
||||
checked_char_cast (newkey));
|
||||
g_free (newkey);
|
||||
add_kvp_value_node(slot_node, "slot:value", (KvpValue*)value);
|
||||
add_kvp_value_node(slot_node, "slot:value", value);
|
||||
}
|
||||
|
||||
xmlNodePtr
|
||||
kvp_frame_to_dom_tree(const char *tag, const KvpFrame *frame)
|
||||
{
|
||||
xmlNodePtr ret;
|
||||
const char ** keys;
|
||||
unsigned int i;
|
||||
|
||||
if (!frame)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!kvp_frame_get_hash(frame))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (g_hash_table_size(kvp_frame_get_hash(frame)) == 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = xmlNewNode(NULL, BAD_CAST tag);
|
||||
|
||||
g_hash_table_foreach_sorted(kvp_frame_get_hash(frame),
|
||||
add_kvp_slot, ret, (GCompareFunc)strcmp);
|
||||
keys = kvp_frame_get_keys(frame);
|
||||
for (i = 0; keys[i]; ++i)
|
||||
add_kvp_slot(keys[i], kvp_frame_get_value(frame, keys[i]), ret);
|
||||
g_free(keys);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -2057,7 +2057,7 @@ xaccSplitMakeStockSplit(Split *s)
|
||||
xaccTransBeginEdit (s->parent);
|
||||
|
||||
s->value = gnc_numeric_zero();
|
||||
kvp_frame_set_str(s->inst.kvp_data, "split-type", "stock-split");
|
||||
kvp_frame_set_string(s->inst.kvp_data, "split-type", "stock-split");
|
||||
SET_GAINS_VDIRTY(s);
|
||||
mark_split(s);
|
||||
qof_instance_set_dirty(QOF_INSTANCE(s));
|
||||
|
@ -2018,7 +2018,7 @@ xaccTransSetTxnType (Transaction *trans, char type)
|
||||
char s[2] = {type, '\0'};
|
||||
g_return_if_fail(trans);
|
||||
xaccTransBeginEdit(trans);
|
||||
kvp_frame_set_str (trans->inst.kvp_data, TRANS_TXN_TYPE_KVP, s);
|
||||
kvp_frame_set_string (trans->inst.kvp_data, TRANS_TXN_TYPE_KVP, s);
|
||||
qof_instance_set_dirty(QOF_INSTANCE(trans));
|
||||
xaccTransCommitEdit(trans);
|
||||
}
|
||||
@ -2041,7 +2041,7 @@ xaccTransSetReadOnly (Transaction *trans, const char *reason)
|
||||
if (trans && reason)
|
||||
{
|
||||
xaccTransBeginEdit(trans);
|
||||
kvp_frame_set_str (trans->inst.kvp_data,
|
||||
kvp_frame_set_string (trans->inst.kvp_data,
|
||||
TRANS_READ_ONLY_REASON, reason);
|
||||
qof_instance_set_dirty(QOF_INSTANCE(trans));
|
||||
xaccTransCommitEdit(trans);
|
||||
@ -2098,7 +2098,7 @@ xaccTransSetAssociation (Transaction *trans, const char *assoc)
|
||||
if (!trans || !assoc) return;
|
||||
xaccTransBeginEdit(trans);
|
||||
|
||||
kvp_frame_set_str (trans->inst.kvp_data, assoc_uri_str, assoc);
|
||||
kvp_frame_set_string (trans->inst.kvp_data, assoc_uri_str, assoc);
|
||||
qof_instance_set_dirty(QOF_INSTANCE(trans));
|
||||
xaccTransCommitEdit(trans);
|
||||
}
|
||||
@ -2117,7 +2117,7 @@ xaccTransSetNotes (Transaction *trans, const char *notes)
|
||||
if (!trans || !notes) return;
|
||||
xaccTransBeginEdit(trans);
|
||||
|
||||
kvp_frame_set_str (trans->inst.kvp_data, trans_notes_str, notes);
|
||||
kvp_frame_set_string (trans->inst.kvp_data, trans_notes_str, notes);
|
||||
qof_instance_set_dirty(QOF_INSTANCE(trans));
|
||||
xaccTransCommitEdit(trans);
|
||||
}
|
||||
|
@ -455,7 +455,7 @@ gnc_lot_set_title (GNCLot *lot, const char *str)
|
||||
if (!lot) return;
|
||||
qof_begin_edit(QOF_INSTANCE(lot));
|
||||
qof_instance_set_dirty(QOF_INSTANCE(lot));
|
||||
kvp_frame_set_str (qof_instance_get_slots(QOF_INSTANCE (lot)),
|
||||
kvp_frame_set_string (qof_instance_get_slots(QOF_INSTANCE (lot)),
|
||||
"/title", str);
|
||||
gnc_lot_commit_edit(lot);
|
||||
}
|
||||
@ -466,7 +466,7 @@ gnc_lot_set_notes (GNCLot *lot, const char *str)
|
||||
if (!lot) return;
|
||||
gnc_lot_begin_edit(lot);
|
||||
qof_instance_set_dirty(QOF_INSTANCE(lot));
|
||||
kvp_frame_set_str (qof_instance_get_slots (QOF_INSTANCE (lot)),
|
||||
kvp_frame_set_string (qof_instance_get_slots (QOF_INSTANCE (lot)),
|
||||
"/notes", str);
|
||||
gnc_lot_commit_edit(lot);
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
/********************************************************************\
|
||||
* 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 *
|
||||
@ -20,11 +22,11 @@
|
||||
\********************************************************************/
|
||||
|
||||
#include "kvp-value.hpp"
|
||||
#include "kvp_frame.hpp"
|
||||
#include <cmath>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <stdexcept>
|
||||
#include "kvp_frame-p.hpp"
|
||||
|
||||
KvpValueImpl::KvpValueImpl(KvpValueImpl const & other) noexcept
|
||||
{
|
||||
@ -75,7 +77,7 @@ KvpValueImpl::get_type() const noexcept
|
||||
return KvpValueType::KVP_TYPE_TIMESPEC;
|
||||
else if (datastore.type() == typeid(GList *))
|
||||
return KvpValueType::KVP_TYPE_GLIST;
|
||||
else if (datastore.type() == typeid(KvpFrame *))
|
||||
else if (datastore.type() == typeid(KvpFrameImpl *))
|
||||
return KvpValueType::KVP_TYPE_FRAME;
|
||||
else if (datastore.type() == typeid(GDate))
|
||||
return KvpValueType::KVP_TYPE_GDATE;
|
||||
@ -208,7 +210,6 @@ KvpValueImpl::to_string() const noexcept
|
||||
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());
|
||||
}
|
||||
|
||||
@ -224,12 +225,11 @@ struct compare_visitor : boost::static_visitor<int>
|
||||
int operator()( T & one, T & two) const
|
||||
{
|
||||
/*This will work for any type that implements < and ==.*/
|
||||
if ( one < two )
|
||||
if (one < two)
|
||||
return -1;
|
||||
else if (one == two)
|
||||
return 0;
|
||||
else
|
||||
if (two < one)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
template <> int compare_visitor::operator()(char * const & one, char * const & two) const
|
||||
@ -265,9 +265,8 @@ template <> int compare_visitor::operator()(double const & one, double const & t
|
||||
if (std::isnan(one) && std::isnan(two))
|
||||
return 0;
|
||||
if (one < two) return -1;
|
||||
/*perhaps we should have tolerance here?*/
|
||||
if (one == two) return 0;
|
||||
return 1;
|
||||
if (two < one) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int compare(const KvpValueImpl & one, const KvpValueImpl & two) noexcept
|
||||
|
@ -1,8 +1,7 @@
|
||||
#ifndef gnc_kvp_value_type
|
||||
#define gnc_kvp_value_type
|
||||
|
||||
/********************************************************************\
|
||||
* kvp-value.hpp -- 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 *
|
||||
@ -22,6 +21,9 @@
|
||||
* *
|
||||
\********************************************************************/
|
||||
|
||||
#ifndef GNC_KVP_VALUE_TYPE
|
||||
#define GNC_KVP_VALUE_TYPE
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "config.h"
|
||||
@ -35,6 +37,7 @@ extern "C"
|
||||
|
||||
struct KvpValueImpl
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Performs a deep copy
|
||||
*/
|
||||
@ -100,7 +103,7 @@ struct KvpValueImpl
|
||||
GncGUID *,
|
||||
Timespec,
|
||||
GList *,
|
||||
boost::recursive_wrapper<KvpFrame *>,
|
||||
KvpFrame *,
|
||||
GDate> datastore;
|
||||
};
|
||||
|
||||
|
@ -1,14 +0,0 @@
|
||||
#ifndef kvp_frame_p_header
|
||||
#define kvp_frame_p_header
|
||||
|
||||
/* Note that we keep the keys for this hash table in the
|
||||
* qof_string_cache, as it is very likely we will see the
|
||||
* same keys over and over again */
|
||||
|
||||
struct _KvpFrame
|
||||
{
|
||||
GHashTable * hash;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -32,108 +32,188 @@ extern "C"
|
||||
#include <string.h>
|
||||
}
|
||||
|
||||
#include <typeinfo>
|
||||
#include <iostream>
|
||||
#include "kvp-value.hpp"
|
||||
#include "kvp_frame-p.hpp"
|
||||
#include "kvp_frame.hpp"
|
||||
#include <typeinfo>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
KvpFrameImpl::KvpFrameImpl(const KvpFrameImpl & rhs) noexcept
|
||||
{
|
||||
std::for_each(rhs.m_valuemap.begin(), rhs.m_valuemap.end(),
|
||||
[this](const map_type::value_type & a)
|
||||
{
|
||||
auto key = static_cast<char *>(qof_string_cache_insert(a.first));
|
||||
auto val = new KvpValueImpl(*a.second);
|
||||
this->m_valuemap.insert({key,val});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
KvpValueImpl * KvpFrameImpl::replace_nc(const char * key, KvpValueImpl * value) noexcept
|
||||
{
|
||||
if (!key) return nullptr;
|
||||
auto spot = m_valuemap.find(key);
|
||||
KvpValueImpl * ret {nullptr};
|
||||
if (spot != m_valuemap.end())
|
||||
{
|
||||
qof_string_cache_remove(spot->first);
|
||||
ret = spot->second;
|
||||
m_valuemap.erase(spot);
|
||||
}
|
||||
|
||||
if (value)
|
||||
{
|
||||
auto cachedkey = static_cast<const char *>(qof_string_cache_insert(key));
|
||||
m_valuemap.insert({cachedkey,value});
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string
|
||||
KvpFrameImpl::to_string() const noexcept
|
||||
{
|
||||
std::ostringstream ret;
|
||||
ret << "{\n";
|
||||
|
||||
std::for_each(m_valuemap.begin(), m_valuemap.end(),
|
||||
[this,&ret](const map_type::value_type &a)
|
||||
{
|
||||
ret << " ";
|
||||
if (a.first)
|
||||
ret << a.first;
|
||||
ret << " => ";
|
||||
if (a.second)
|
||||
ret << a.second->to_string();
|
||||
ret << ",\n";
|
||||
}
|
||||
);
|
||||
|
||||
ret << "}\n";
|
||||
return ret.str();
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
KvpFrameImpl::get_keys() const noexcept
|
||||
{
|
||||
std::vector<std::string> ret;
|
||||
std::for_each(m_valuemap.begin(), m_valuemap.end(),
|
||||
[&ret](const KvpFrameImpl::map_type::value_type &a)
|
||||
{
|
||||
ret.push_back(a.first);
|
||||
}
|
||||
);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
KvpFrameImpl::for_each_slot(void (*proc)(const char *key, KvpValue *value, void * data),
|
||||
void *data) const noexcept
|
||||
{
|
||||
if (!proc) return;
|
||||
std::for_each (m_valuemap.begin(), m_valuemap.end(),
|
||||
[proc,data](const KvpFrameImpl::map_type::value_type & a)
|
||||
{
|
||||
proc (a.first, a.second, data);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
KvpValueImpl *
|
||||
KvpFrameImpl::get_slot(const char * key) const noexcept
|
||||
{
|
||||
auto spot = m_valuemap.find(key);
|
||||
if (spot == m_valuemap.end())
|
||||
return nullptr;
|
||||
return spot->second;
|
||||
}
|
||||
|
||||
int compare(const KvpFrameImpl * one, const KvpFrameImpl * two) noexcept
|
||||
{
|
||||
if (one && !two) return 1;
|
||||
if (!one && two) return -1;
|
||||
if (!one && !two) return 0;
|
||||
return compare(*one, *two);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the first KvpFrameImpl has an item that the second does not, 1 is returned.
|
||||
* The first item within the two KvpFrameImpl that is not similar, that comparison is returned.
|
||||
* If all the items within the first KvpFrameImpl match items within the second, but the
|
||||
* second has more elements, -1 is returned.
|
||||
* Otherwise, 0 is returned.
|
||||
*/
|
||||
int compare(const KvpFrameImpl & one, const KvpFrameImpl & two) noexcept
|
||||
{
|
||||
for (const auto & a : one.m_valuemap)
|
||||
{
|
||||
auto otherspot = two.m_valuemap.find(a.first);
|
||||
if (otherspot == two.m_valuemap.end())
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
auto comparison = compare(a.second,otherspot->second);
|
||||
|
||||
if (comparison != 0)
|
||||
return comparison;
|
||||
}
|
||||
|
||||
if (one.m_valuemap.size() < two.m_valuemap.size())
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This static indicates the debugging module that this .o belongs to. */
|
||||
static QofLogModule log_module = QOF_MOD_KVP;
|
||||
|
||||
/* *******************************************************************
|
||||
* KvpFrame functions
|
||||
********************************************************************/
|
||||
|
||||
static guint
|
||||
kvp_hash_func(gconstpointer v)
|
||||
{
|
||||
return g_str_hash(v);
|
||||
}
|
||||
|
||||
static gint
|
||||
kvp_comp_func(gconstpointer v, gconstpointer v2)
|
||||
{
|
||||
return g_str_equal(v, v2);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
init_frame_body_if_needed(KvpFrame *f)
|
||||
{
|
||||
if (!f->hash)
|
||||
{
|
||||
f->hash = g_hash_table_new(&kvp_hash_func, &kvp_comp_func);
|
||||
}
|
||||
return(f->hash != NULL);
|
||||
}
|
||||
|
||||
KvpFrame *
|
||||
kvp_frame_new(void)
|
||||
{
|
||||
KvpFrame * retval = g_new0(KvpFrame, 1);
|
||||
|
||||
/* Save space until the frame is actually used */
|
||||
retval->hash = NULL;
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void
|
||||
kvp_frame_delete_worker(gpointer key, gpointer value, G_GNUC_UNUSED gpointer user_data)
|
||||
{
|
||||
qof_string_cache_remove(key);
|
||||
kvp_value_delete(static_cast<KvpValue *>(value));
|
||||
auto ret = new KvpFrameImpl();
|
||||
return static_cast<KvpFrame *>(ret);
|
||||
}
|
||||
|
||||
void
|
||||
kvp_frame_delete(KvpFrame * frame)
|
||||
{
|
||||
if (!frame) return;
|
||||
auto realframe = static_cast<KvpFrameImpl *>(frame);
|
||||
delete realframe;
|
||||
}
|
||||
|
||||
if (frame->hash)
|
||||
{
|
||||
/* free any allocated resource for frame or its children */
|
||||
g_hash_table_foreach(frame->hash, & kvp_frame_delete_worker,
|
||||
(gpointer)frame);
|
||||
|
||||
/* delete the hash table */
|
||||
g_hash_table_destroy(frame->hash);
|
||||
frame->hash = NULL;
|
||||
}
|
||||
g_free(frame);
|
||||
const char **
|
||||
kvp_frame_get_keys(const KvpFrame * frame)
|
||||
{
|
||||
if (!frame) return nullptr;
|
||||
auto realframe = static_cast<KvpFrameImpl const *>(frame);
|
||||
const auto & keys = realframe->get_keys();
|
||||
const char ** ret = g_new(const char *, keys.size() + 1);
|
||||
unsigned int spot {0};
|
||||
std::for_each(keys.begin(), keys.end(),
|
||||
[&ret,&spot](const std::string &a)
|
||||
{
|
||||
ret[spot++] = g_strdup(a.c_str());
|
||||
}
|
||||
);
|
||||
ret[keys.size()] = nullptr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
kvp_frame_is_empty(const KvpFrame * frame)
|
||||
{
|
||||
if (!frame) return TRUE;
|
||||
if (!frame->hash) return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
kvp_frame_copy_worker(gpointer key, gpointer value, gpointer user_data)
|
||||
{
|
||||
KvpFrame * dest = (KvpFrame *)user_data;
|
||||
g_hash_table_insert(dest->hash,
|
||||
qof_string_cache_insert(key),
|
||||
static_cast<void*>(kvp_value_copy(static_cast<KvpValue*>(value))));
|
||||
auto realframe = static_cast<KvpFrameImpl const *>(frame);
|
||||
return realframe->get_keys().size() == 0;
|
||||
}
|
||||
|
||||
KvpFrame *
|
||||
kvp_frame_copy(const KvpFrame * frame)
|
||||
{
|
||||
KvpFrame * retval = kvp_frame_new();
|
||||
|
||||
if (!frame) return retval;
|
||||
|
||||
if (frame->hash)
|
||||
{
|
||||
if (!init_frame_body_if_needed(retval)) return(NULL);
|
||||
g_hash_table_foreach(frame->hash,
|
||||
& kvp_frame_copy_worker,
|
||||
(gpointer)retval);
|
||||
}
|
||||
return retval;
|
||||
auto ret = new KvpFrameImpl(*static_cast<KvpFrameImpl const *>(frame));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Replace the old value with the new value. Return the old value.
|
||||
@ -144,33 +224,11 @@ static KvpValue *
|
||||
kvp_frame_replace_slot_nc (KvpFrame * frame, const char * slot,
|
||||
KvpValue * new_value)
|
||||
{
|
||||
gpointer orig_key;
|
||||
gpointer orig_value = NULL;
|
||||
int key_exists;
|
||||
if (!frame) return NULL;
|
||||
|
||||
if (!frame || !slot) return NULL;
|
||||
if (!init_frame_body_if_needed(frame)) return NULL; /* Error ... */
|
||||
|
||||
key_exists = g_hash_table_lookup_extended(frame->hash, slot,
|
||||
& orig_key, & orig_value);
|
||||
if (key_exists)
|
||||
{
|
||||
g_hash_table_remove(frame->hash, slot);
|
||||
qof_string_cache_remove(orig_key);
|
||||
}
|
||||
else
|
||||
{
|
||||
orig_value = NULL;
|
||||
}
|
||||
|
||||
if (new_value)
|
||||
{
|
||||
g_hash_table_insert(frame->hash,
|
||||
qof_string_cache_insert((gpointer) slot),
|
||||
new_value);
|
||||
}
|
||||
|
||||
return (KvpValue *) orig_value;
|
||||
auto realframe = static_cast<KvpFrameImpl *>(frame);
|
||||
auto realnewvalue = static_cast<KvpValueImpl *>(new_value);
|
||||
return realframe->replace_nc(slot, realnewvalue);
|
||||
}
|
||||
|
||||
/* Passing in a null value into this routine has the effect
|
||||
@ -549,15 +607,11 @@ kvp_frame_set_slot_nc(KvpFrame * frame, const char * slot,
|
||||
KvpValue *
|
||||
kvp_frame_get_slot(const KvpFrame * frame, const char * slot)
|
||||
{
|
||||
KvpValue *v;
|
||||
if (!frame) return NULL;
|
||||
if (!frame->hash) return NULL;
|
||||
v = static_cast<KvpValue*>(g_hash_table_lookup(frame->hash, slot));
|
||||
return v;
|
||||
auto realframe = static_cast<const KvpFrameImpl *>(frame);
|
||||
return realframe->get_slot(slot);
|
||||
}
|
||||
|
||||
/* ============================================================ */
|
||||
|
||||
void
|
||||
kvp_frame_set_slot_path (KvpFrame *frame,
|
||||
KvpValue * new_value,
|
||||
@ -1060,10 +1114,8 @@ kvp_frame_for_each_slot(KvpFrame *f,
|
||||
gpointer data)
|
||||
{
|
||||
if (!f) return;
|
||||
if (!proc) return;
|
||||
if (!(f->hash)) return;
|
||||
|
||||
g_hash_table_foreach(f->hash, (GHFunc) proc, data);
|
||||
auto realframe = static_cast<KvpFrameImpl *>(f);
|
||||
realframe->for_each_slot(proc, data);
|
||||
}
|
||||
|
||||
int
|
||||
@ -1074,59 +1126,12 @@ kvp_value_compare(const KvpValue * okva, const KvpValue * okvb)
|
||||
return compare(kva, kvb);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gint compare;
|
||||
KvpFrame *other_frame;
|
||||
} kvp_frame_cmp_status;
|
||||
|
||||
static void
|
||||
kvp_frame_compare_helper(const char *key, KvpValue * val, gpointer data)
|
||||
{
|
||||
kvp_frame_cmp_status *status = (kvp_frame_cmp_status *) data;
|
||||
if (status->compare == 0)
|
||||
{
|
||||
KvpFrame *other_frame = status->other_frame;
|
||||
KvpValue *other_val = kvp_frame_get_slot(other_frame, key);
|
||||
|
||||
if (other_val)
|
||||
{
|
||||
status->compare = kvp_value_compare(val, other_val);
|
||||
}
|
||||
else
|
||||
{
|
||||
status->compare = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gint
|
||||
kvp_frame_compare(const KvpFrame *fa, const KvpFrame *fb)
|
||||
{
|
||||
kvp_frame_cmp_status status;
|
||||
|
||||
if (fa == fb) return 0;
|
||||
/* nothing is always less than something */
|
||||
if (!fa && fb) return -1;
|
||||
if (fa && !fb) return 1;
|
||||
|
||||
/* nothing is always less than something */
|
||||
if (!fa->hash && fb->hash) return -1;
|
||||
if (fa->hash && !fb->hash) return 1;
|
||||
|
||||
status.compare = 0;
|
||||
status.other_frame = (KvpFrame *) fb;
|
||||
|
||||
kvp_frame_for_each_slot((KvpFrame *) fa, kvp_frame_compare_helper, &status);
|
||||
|
||||
if (status.compare != 0)
|
||||
return status.compare;
|
||||
|
||||
status.other_frame = (KvpFrame *) fa;
|
||||
|
||||
kvp_frame_for_each_slot((KvpFrame *) fb, kvp_frame_compare_helper, &status);
|
||||
|
||||
return(-status.compare);
|
||||
auto realone = static_cast<const KvpFrameImpl *>(fa);
|
||||
auto realtwo = static_cast<const KvpFrameImpl *>(fb);
|
||||
return compare(realone, realtwo);
|
||||
}
|
||||
|
||||
char *
|
||||
@ -1137,7 +1142,6 @@ kvp_value_to_string(const KvpValue * val)
|
||||
return realval->to_string();
|
||||
}
|
||||
|
||||
/* struct for kvp frame static funtion testing*/
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
@ -1163,53 +1167,13 @@ init_static_test_pointers( void )
|
||||
p_get_trailer_or_null = get_trailer_or_null;
|
||||
}
|
||||
|
||||
/* ----- */
|
||||
|
||||
static void
|
||||
kvp_frame_to_string_helper(gpointer key, gpointer value, gpointer data)
|
||||
{
|
||||
gchar *tmp_val;
|
||||
gchar **str = (gchar**)data;
|
||||
gchar *old_data = *str;
|
||||
|
||||
tmp_val = kvp_value_to_string((KvpValue *)value);
|
||||
|
||||
*str = g_strdup_printf("%s %s => %s,\n",
|
||||
*str ? *str : "",
|
||||
key ? (char *) key : "",
|
||||
tmp_val ? tmp_val : "");
|
||||
|
||||
g_free(old_data);
|
||||
g_free(tmp_val);
|
||||
}
|
||||
|
||||
gchar*
|
||||
char*
|
||||
kvp_frame_to_string(const KvpFrame *frame)
|
||||
{
|
||||
gchar *tmp1;
|
||||
|
||||
g_return_val_if_fail (frame != NULL, NULL);
|
||||
|
||||
tmp1 = g_strdup_printf("{\n");
|
||||
|
||||
if (frame->hash)
|
||||
g_hash_table_foreach(frame->hash, kvp_frame_to_string_helper, &tmp1);
|
||||
|
||||
{
|
||||
gchar *tmp2;
|
||||
tmp2 = g_strdup_printf("%s}\n", tmp1);
|
||||
g_free(tmp1);
|
||||
tmp1 = tmp2;
|
||||
}
|
||||
|
||||
return tmp1;
|
||||
}
|
||||
|
||||
GHashTable*
|
||||
kvp_frame_get_hash(const KvpFrame *frame)
|
||||
{
|
||||
g_return_val_if_fail (frame != NULL, NULL);
|
||||
return frame->hash;
|
||||
auto realframe = static_cast<KvpFrameImpl const *>(frame);
|
||||
/*We'll use g_strdup
|
||||
because it will be freed using g_free.*/
|
||||
return g_strdup(realframe->to_string().c_str());
|
||||
}
|
||||
|
||||
static GValue *gvalue_from_kvp_value (KvpValue *);
|
||||
|
@ -73,7 +73,7 @@ extern "C"
|
||||
#define QOF_MOD_KVP "qof.kvp"
|
||||
|
||||
/** Opaque frame structure */
|
||||
typedef struct _KvpFrame KvpFrame;
|
||||
typedef struct KvpFrameImpl KvpFrame;
|
||||
|
||||
/** A KvpValue is a union with possible types enumerated in the
|
||||
* KvpValueType enum. */
|
||||
@ -138,6 +138,17 @@ gboolean kvp_frame_is_empty(const KvpFrame * frame);
|
||||
@{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Retrieve the keys for the frame.
|
||||
*
|
||||
* Returns a null-terminated array of the keys which can be
|
||||
* used to look up values and determine the pairs in this frame.
|
||||
*
|
||||
* The caller should free the array using g_free, but should
|
||||
* not free the keys.
|
||||
*/
|
||||
const char ** kvp_frame_get_keys(const KvpFrame * frame);
|
||||
|
||||
/** store the value of the
|
||||
* gint64 at the indicated path. If not all frame components of
|
||||
* the path exist, they are created.
|
||||
@ -169,12 +180,6 @@ void kvp_frame_set_numeric(KvpFrame * frame, const gchar * path, gnc_numeric nva
|
||||
*/
|
||||
void kvp_frame_set_timespec(KvpFrame * frame, const gchar * path, Timespec ts);
|
||||
|
||||
/** \deprecated
|
||||
|
||||
Use kvp_frame_set_string instead of kvp_frame_set_str
|
||||
*/
|
||||
#define kvp_frame_set_str kvp_frame_set_string
|
||||
|
||||
/** \brief Store a copy of the string at the indicated path.
|
||||
|
||||
* If not all frame components of the path
|
||||
@ -572,7 +577,6 @@ void kvp_frame_for_each_slot(KvpFrame *f,
|
||||
|
||||
/** Internal helper routines, you probably shouldn't be using these. */
|
||||
gchar* kvp_frame_to_string(const KvpFrame *frame);
|
||||
GHashTable* kvp_frame_get_hash(const KvpFrame *frame);
|
||||
|
||||
/** KvpItem: GValue Exchange
|
||||
* \brief Transfer of KVP to and from GValue, with the key
|
||||
|
80
src/libqof/qof/kvp_frame.hpp
Normal file
80
src/libqof/qof/kvp_frame.hpp
Normal file
@ -0,0 +1,80 @@
|
||||
/********************************************************************\
|
||||
* kvp-frame.hpp -- 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 *
|
||||
* *
|
||||
\********************************************************************/
|
||||
|
||||
#ifndef GNC_KVP_FRAME_TYPE
|
||||
#define GNC_KVP_FRAME_TYPE
|
||||
|
||||
#include "kvp-value.hpp"
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
|
||||
class cstring_comparer
|
||||
{
|
||||
public:
|
||||
/* Returns true if one is less than two. */
|
||||
bool operator()(const char * one, const char * two) const
|
||||
{
|
||||
auto ret = std::strcmp(one, two) < 0;
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
struct KvpFrameImpl
|
||||
{
|
||||
typedef std::map<const char *, KvpValueImpl *, cstring_comparer> map_type;
|
||||
|
||||
public:
|
||||
KvpFrameImpl() noexcept {};
|
||||
|
||||
/**
|
||||
* Performs a deep copy.
|
||||
*/
|
||||
KvpFrameImpl(const KvpFrameImpl &) noexcept;
|
||||
|
||||
/**
|
||||
* Replaces the KvpValueImpl at the specified spot, and returns
|
||||
* the old KvpValueImpl which used to occupy the spot.
|
||||
*
|
||||
* If no KvpValueImpl was at the spot, nullptr is returned.
|
||||
*/
|
||||
KvpValueImpl * replace_nc(const char * key, KvpValueImpl * newvalue) noexcept;
|
||||
|
||||
std::string to_string() const noexcept;
|
||||
|
||||
std::vector<std::string> get_keys() const noexcept;
|
||||
|
||||
KvpValueImpl * get_slot(const char * key) const noexcept;
|
||||
|
||||
void for_each_slot(void (*proc)(const char *key, KvpValue *value, void * data), void *data) const noexcept;
|
||||
|
||||
friend int compare(const KvpFrameImpl &, const KvpFrameImpl &) noexcept;
|
||||
|
||||
private:
|
||||
map_type m_valuemap;
|
||||
};
|
||||
|
||||
int compare (const KvpFrameImpl &, const KvpFrameImpl &) noexcept;
|
||||
int compare (const KvpFrameImpl *, const KvpFrameImpl *) noexcept;
|
||||
#endif
|
@ -185,6 +185,7 @@ test_kvp_frame_copy( Fixture *fixture, gconstpointer pData )
|
||||
g_assert( to_copy );
|
||||
g_assert( !kvp_frame_is_empty( to_copy ) );
|
||||
g_assert( to_copy != fixture->frame );
|
||||
|
||||
g_assert_cmpint( kvp_frame_compare( fixture->frame, to_copy ), == , 0 );
|
||||
copy_gint64 = kvp_frame_get_gint64( to_copy, "gint64-type" );
|
||||
g_assert( ©_gint64 != &test_gint64 );
|
||||
@ -1114,7 +1115,6 @@ test_kvp_frame_to_string( Fixture *fixture, gconstpointer pData )
|
||||
static void
|
||||
test_kvp_frame_set_slot_path( Fixture *fixture, gconstpointer pData )
|
||||
{
|
||||
GHashTable *frame_hash = NULL;
|
||||
KvpValue *input_value, *output_value;
|
||||
|
||||
g_assert( fixture->frame );
|
||||
@ -1136,9 +1136,6 @@ test_kvp_frame_set_slot_path( Fixture *fixture, gconstpointer pData )
|
||||
g_assert( output_value );
|
||||
g_assert( input_value != output_value ); /* copied */
|
||||
g_assert_cmpint( kvp_value_compare( output_value, input_value ), == , 0 ); /* old value removed */
|
||||
frame_hash = kvp_frame_get_hash( fixture->frame );
|
||||
g_assert( frame_hash );
|
||||
g_assert_cmpint( g_hash_table_size( frame_hash ), == , 1 ); /* be sure it was replaced */
|
||||
kvp_value_delete( input_value );
|
||||
|
||||
g_test_message( "Test when existing path elements are not frames" );
|
||||
@ -1146,7 +1143,6 @@ test_kvp_frame_set_slot_path( Fixture *fixture, gconstpointer pData )
|
||||
kvp_frame_set_slot_path( fixture->frame, input_value, "test", "test2", NULL );
|
||||
g_assert( kvp_frame_get_slot_path( fixture->frame, "test2", NULL ) == NULL );/* was not added */
|
||||
g_assert_cmpint( kvp_value_compare( output_value, kvp_frame_get_slot_path( fixture->frame, "test", NULL ) ), == , 0 ); /* nothing changed */
|
||||
g_assert_cmpint( g_hash_table_size( frame_hash ), == , 1 ); /* didn't change */
|
||||
kvp_value_delete( input_value );
|
||||
|
||||
g_test_message( "Test frames are created along the path when needed" );
|
||||
@ -1159,7 +1155,6 @@ test_kvp_frame_set_slot_path( Fixture *fixture, gconstpointer pData )
|
||||
g_assert( output_value );
|
||||
g_assert( input_value != output_value ); /* copied */
|
||||
g_assert_cmpint( kvp_value_compare( output_value, input_value ), == , 0 );
|
||||
g_assert_cmpint( g_hash_table_size( frame_hash ), == , 2 );
|
||||
kvp_value_delete( input_value );
|
||||
}
|
||||
|
||||
@ -1168,7 +1163,6 @@ test_kvp_frame_set_slot_path_gslist( Fixture *fixture, gconstpointer pData )
|
||||
{
|
||||
/* similar to previous test except path is passed as GSList*/
|
||||
GSList *path_list = NULL;
|
||||
GHashTable *frame_hash = NULL;
|
||||
KvpValue *input_value, *output_value;
|
||||
|
||||
g_assert( fixture->frame );
|
||||
@ -1191,9 +1185,6 @@ test_kvp_frame_set_slot_path_gslist( Fixture *fixture, gconstpointer pData )
|
||||
g_assert( output_value );
|
||||
g_assert( input_value != output_value ); /* copied */
|
||||
g_assert_cmpint( kvp_value_compare( output_value, input_value ), == , 0 ); /* old value removed */
|
||||
frame_hash = kvp_frame_get_hash( fixture->frame );
|
||||
g_assert( frame_hash );
|
||||
g_assert_cmpint( g_hash_table_size( frame_hash ), == , 1 ); /* be sure it was replaced */
|
||||
kvp_value_delete( input_value );
|
||||
|
||||
g_test_message( "Test when existing path elements are not frames" );
|
||||
@ -1202,7 +1193,6 @@ test_kvp_frame_set_slot_path_gslist( Fixture *fixture, gconstpointer pData )
|
||||
kvp_frame_set_slot_path_gslist( fixture->frame, input_value, path_list );
|
||||
g_assert( kvp_frame_get_slot_path( fixture->frame, "test2", NULL ) == NULL );/* was not added */
|
||||
g_assert_cmpint( kvp_value_compare( output_value, kvp_frame_get_slot_path( fixture->frame, "test", NULL ) ), == , 0 ); /* nothing changed */
|
||||
g_assert_cmpint( g_hash_table_size( frame_hash ), == , 1 ); /* didn't change */
|
||||
kvp_value_delete( input_value );
|
||||
|
||||
g_test_message( "Test frames are created along the path when needed" );
|
||||
@ -1217,7 +1207,6 @@ test_kvp_frame_set_slot_path_gslist( Fixture *fixture, gconstpointer pData )
|
||||
g_assert( output_value );
|
||||
g_assert( input_value != output_value ); /* copied */
|
||||
g_assert_cmpint( kvp_value_compare( output_value, input_value ), == , 0 );
|
||||
g_assert_cmpint( g_hash_table_size( frame_hash ), == , 2 );
|
||||
kvp_value_delete( input_value );
|
||||
|
||||
g_slist_free( path_list );
|
||||
@ -1226,8 +1215,8 @@ test_kvp_frame_set_slot_path_gslist( Fixture *fixture, gconstpointer pData )
|
||||
static void
|
||||
test_kvp_frame_replace_slot_nc( Fixture *fixture, gconstpointer pData )
|
||||
{
|
||||
GHashTable *frame_hash;
|
||||
KvpValue *orig_value, *orig_value2, *copy_value;
|
||||
KvpValue *orig_value, *orig_value2;
|
||||
const char ** keys;
|
||||
/* test indirectly static function kvp_frame_replace_slot_nc */
|
||||
g_assert( fixture->frame );
|
||||
g_assert( kvp_frame_is_empty( fixture->frame ) );
|
||||
@ -1236,23 +1225,23 @@ test_kvp_frame_replace_slot_nc( Fixture *fixture, gconstpointer pData )
|
||||
orig_value = kvp_value_new_gint64( 2 );
|
||||
kvp_frame_set_slot( fixture->frame, "test", orig_value );
|
||||
g_assert( !kvp_frame_is_empty( fixture->frame ) );
|
||||
frame_hash = kvp_frame_get_hash( fixture->frame );
|
||||
g_assert( frame_hash );
|
||||
g_assert_cmpint( g_hash_table_size( frame_hash ), == , 1 );
|
||||
copy_value = g_hash_table_lookup( frame_hash, "test" );
|
||||
g_assert( orig_value != copy_value );
|
||||
g_assert_cmpint( kvp_value_compare( orig_value, copy_value ), == , 0 );
|
||||
keys = kvp_frame_get_keys( fixture->frame );
|
||||
g_assert( keys );
|
||||
g_assert( !keys[1] );
|
||||
g_assert( orig_value != kvp_frame_get_value( fixture->frame, keys[0] ) );
|
||||
g_assert_cmpint( kvp_value_compare( kvp_frame_get_value( fixture->frame, keys[0] ), orig_value ), ==, 0 );
|
||||
g_free( keys );
|
||||
|
||||
g_test_message( "Test when value is replaced" );
|
||||
orig_value2 = kvp_value_new_gint64( 5 );
|
||||
kvp_frame_set_slot( fixture->frame, "test", orig_value2 );
|
||||
frame_hash = kvp_frame_get_hash( fixture->frame );
|
||||
g_assert( frame_hash );
|
||||
g_assert_cmpint( g_hash_table_size( frame_hash ), == , 1 );
|
||||
copy_value = g_hash_table_lookup( frame_hash, "test" );
|
||||
g_assert( orig_value2 != copy_value );
|
||||
g_assert_cmpint( kvp_value_compare( orig_value2, copy_value ), == , 0 );
|
||||
g_assert_cmpint( kvp_value_compare( orig_value, copy_value ), != , 0 );
|
||||
keys = kvp_frame_get_keys( fixture->frame );
|
||||
g_assert( keys );
|
||||
g_assert( !keys[1] );
|
||||
g_assert( orig_value != kvp_frame_get_value( fixture->frame, keys[0] ) );
|
||||
g_assert_cmpint( kvp_value_compare( kvp_frame_get_value( fixture->frame, keys[0] ), orig_value2 ), ==, 0 );
|
||||
g_assert_cmpint( kvp_value_compare( kvp_frame_get_value( fixture->frame, keys[0] ), orig_value ), !=, 0 );
|
||||
g_free( keys );
|
||||
|
||||
kvp_value_delete( orig_value );
|
||||
kvp_value_delete( orig_value2 );
|
||||
@ -1653,6 +1642,33 @@ test_kvp_frame_set_gvalue (Fixture *fixture, gconstpointer pData)
|
||||
kvp_frame_delete (n_frame);
|
||||
}
|
||||
|
||||
static void
|
||||
test_kvp_frame_get_keys( void )
|
||||
{
|
||||
KvpFrame * frame = kvp_frame_new();
|
||||
const char * key1 = "number one";
|
||||
const char * key2 = "number one/number two";
|
||||
const char * key3 = "number three";
|
||||
const char * val1 = "Value1";
|
||||
const char * val2 = "Value2";
|
||||
const char * val3 = "Value3";
|
||||
unsigned int spot = 0;
|
||||
const char ** keys;
|
||||
kvp_frame_set_string(frame, key1, val1);
|
||||
kvp_frame_set_string(frame, key2, val2);
|
||||
kvp_frame_set_string(frame, key3, val3);
|
||||
keys = kvp_frame_get_keys(frame);
|
||||
|
||||
g_assert(keys);
|
||||
g_assert(keys[spot]);
|
||||
g_assert(strcmp(keys[spot++], val1));
|
||||
g_assert(keys[spot]);
|
||||
g_assert(strcmp(keys[spot++], val3));
|
||||
g_assert(!keys[spot]);
|
||||
|
||||
g_free(keys);
|
||||
kvp_frame_delete(frame);
|
||||
}
|
||||
|
||||
void
|
||||
test_suite_kvp_frame( void )
|
||||
@ -1675,6 +1691,7 @@ test_suite_kvp_frame( void )
|
||||
GNC_TEST_ADD( suitename, "kvp frame set slot path", Fixture, NULL, setup, test_kvp_frame_set_slot_path, teardown );
|
||||
GNC_TEST_ADD( suitename, "kvp frame set slot path gslist", Fixture, NULL, setup, test_kvp_frame_set_slot_path_gslist, teardown );
|
||||
GNC_TEST_ADD( suitename, "kvp frame replace slot nc", Fixture, NULL, setup, test_kvp_frame_replace_slot_nc, teardown );
|
||||
GNC_TEST_ADD_FUNC( suitename, "kvp frame get keys", test_kvp_frame_get_keys );
|
||||
GNC_TEST_ADD( suitename, "get trailer make", Fixture, NULL, setup_static, test_get_trailer_make, teardown_static );
|
||||
GNC_TEST_ADD( suitename, "kvp value glist to string", Fixture, NULL, setup_static, test_kvp_value_glist_to_string, teardown_static );
|
||||
GNC_TEST_ADD( suitename, "get or make", Fixture, NULL, setup_static, test_get_or_make, teardown_static );
|
||||
|
Loading…
Reference in New Issue
Block a user