gnucash master: Implement KvpFrame in C++ using std::vector
John Ralls
jralls at code.gnucash.org
Mon Nov 3 15:29:11 EST 2014
Updated via https://github.com/Gnucash/gnucash/commit/076f1fb2 (commit)
from https://github.com/Gnucash/gnucash/commit/6c2a42bf (commit)
commit 076f1fb25df02602a9d1ed5ecdebc4a1f3d18940
Author: lmat <dartme18 at gmail.com>
Date: Fri Oct 3 22:05:37 2014 -0400
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).
diff --git a/src/backend/xml/sixtp-dom-generators.c b/src/backend/xml/sixtp-dom-generators.c
index 0fc8e74..6217697 100644
--- a/src/backend/xml/sixtp-dom-generators.c
+++ b/src/backend/xml/sixtp-dom-generators.c
@@ -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;
}
diff --git a/src/engine/Split.c b/src/engine/Split.c
index e5cf17d..345d30a 100644
--- a/src/engine/Split.c
+++ b/src/engine/Split.c
@@ -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));
diff --git a/src/engine/Transaction.c b/src/engine/Transaction.c
index 61a6f57..01caf28 100644
--- a/src/engine/Transaction.c
+++ b/src/engine/Transaction.c
@@ -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);
}
diff --git a/src/engine/gnc-lot.c b/src/engine/gnc-lot.c
index b0474e4..f0c951a 100644
--- a/src/engine/gnc-lot.c
+++ b/src/engine/gnc-lot.c
@@ -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);
}
diff --git a/src/libqof/qof/kvp-value.cpp b/src/libqof/qof/kvp-value.cpp
index 4df79c3..f92e711 100644
--- a/src/libqof/qof/kvp-value.cpp
+++ b/src/libqof/qof/kvp-value.cpp
@@ -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
diff --git a/src/libqof/qof/kvp-value.hpp b/src/libqof/qof/kvp-value.hpp
index 4c1b1ae..bf1b616 100644
--- a/src/libqof/qof/kvp-value.hpp
+++ b/src/libqof/qof/kvp-value.hpp
@@ -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;
};
diff --git a/src/libqof/qof/kvp_frame-p.hpp b/src/libqof/qof/kvp_frame-p.hpp
deleted file mode 100644
index 3678083..0000000
--- a/src/libqof/qof/kvp_frame-p.hpp
+++ /dev/null
@@ -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
diff --git a/src/libqof/qof/kvp_frame.cpp b/src/libqof/qof/kvp_frame.cpp
index db20f9c..4ba546e 100644
--- a/src/libqof/qof/kvp_frame.cpp
+++ b/src/libqof/qof/kvp_frame.cpp
@@ -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});
+ }
+ );
+}
-/* This static indicates the debugging module that this .o belongs to. */
-static QofLogModule log_module = QOF_MOD_KVP;
+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);
+ }
-/* *******************************************************************
- * KvpFrame functions
- ********************************************************************/
+ if (value)
+ {
+ auto cachedkey = static_cast<const char *>(qof_string_cache_insert(key));
+ m_valuemap.insert({cachedkey,value});
+ }
-static guint
-kvp_hash_func(gconstpointer v)
+ return ret;
+}
+
+std::string
+KvpFrameImpl::to_string() const noexcept
{
- return g_str_hash(v);
+ 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();
}
-static gint
-kvp_comp_func(gconstpointer v, gconstpointer v2)
+std::vector<std::string>
+KvpFrameImpl::get_keys() const noexcept
{
- return g_str_equal(v, v2);
+ 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;
}
-static gboolean
-init_frame_body_if_needed(KvpFrame *f)
+void
+KvpFrameImpl::for_each_slot(void (*proc)(const char *key, KvpValue *value, void * data),
+ void *data) const noexcept
{
- if (!f->hash)
- {
- f->hash = g_hash_table_new(&kvp_hash_func, &kvp_comp_func);
- }
- return(f->hash != NULL);
+ 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);
+ }
+ );
}
-KvpFrame *
-kvp_frame_new(void)
+KvpValueImpl *
+KvpFrameImpl::get_slot(const char * key) const noexcept
{
- KvpFrame * retval = g_new0(KvpFrame, 1);
+ auto spot = m_valuemap.find(key);
+ if (spot == m_valuemap.end())
+ return nullptr;
+ return spot->second;
+}
- /* Save space until the frame is actually used */
- retval->hash = NULL;
- return retval;
+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);
}
-static void
-kvp_frame_delete_worker(gpointer key, gpointer value, G_GNUC_UNUSED gpointer user_data)
+/**
+ * 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
{
- qof_string_cache_remove(key);
- kvp_value_delete(static_cast<KvpValue *>(value));
+ 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 *
+kvp_frame_new(void)
+{
+ 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 || !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);
- }
+ if (!frame) return NULL;
- 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 *);
diff --git a/src/libqof/qof/kvp_frame.h b/src/libqof/qof/kvp_frame.h
index aa8ed2c..0f9423f 100644
--- a/src/libqof/qof/kvp_frame.h
+++ b/src/libqof/qof/kvp_frame.h
@@ -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
diff --git a/src/libqof/qof/kvp_frame.hpp b/src/libqof/qof/kvp_frame.hpp
new file mode 100644
index 0000000..3f5e3dc
--- /dev/null
+++ b/src/libqof/qof/kvp_frame.hpp
@@ -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 at 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
diff --git a/src/libqof/qof/test/test-kvp_frame.c b/src/libqof/qof/test/test-kvp_frame.c
index 1c59fe5..2dc272c 100644
--- a/src/libqof/qof/test/test-kvp_frame.c
+++ b/src/libqof/qof/test/test-kvp_frame.c
@@ -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 );
Summary of changes:
src/backend/xml/sixtp-dom-generators.c | 37 ++--
src/engine/Split.c | 2 +-
src/engine/Transaction.c | 8 +-
src/engine/gnc-lot.c | 4 +-
src/libqof/qof/kvp-value.cpp | 19 +-
src/libqof/qof/kvp-value.hpp | 11 +-
src/libqof/qof/kvp_frame-p.hpp | 14 --
src/libqof/qof/kvp_frame.cpp | 358 +++++++++++++++------------------
src/libqof/qof/kvp_frame.h | 20 +-
src/libqof/qof/kvp_frame.hpp | 80 ++++++++
src/libqof/qof/test/test-kvp_frame.c | 71 ++++---
11 files changed, 336 insertions(+), 288 deletions(-)
delete mode 100644 src/libqof/qof/kvp_frame-p.hpp
create mode 100644 src/libqof/qof/kvp_frame.hpp
More information about the gnucash-changes
mailing list