GnuCash  4.8a-176-g88ecf8dd1
kvp-frame.cpp
1 /********************************************************************
2  * kvp_frame.cpp -- Implements a key-value frame system *
3  * Copyright (C) 2000 Bill Gribble *
4  * Copyright (C) 2001,2003 Linas Vepstas <linas@linas.org> *
5  * *
6  * This program is free software; you can redistribute it and/or *
7  * modify it under the terms of the GNU General Public License as *
8  * published by the Free Software Foundation; either version 2 of *
9  * the License, or (at your option) any later version. *
10  * *
11  * This program is distributed in the hope that it will be useful, *
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14  * GNU General Public License for more details. *
15  * *
16  * You should have received a copy of the GNU General Public License*
17  * along with this program; if not, contact: *
18  * *
19  * Free Software Foundation Voice: +1-617-542-5942 *
20  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
21  * Boston, MA 02110-1301, USA gnu@gnu.org *
22  * *
23  ********************************************************************/
24 
25 extern "C"
26 {
27 #include <config.h>
28 #include "qof.h"
29 #include <glib.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <string.h>
33 }
34 
35 #include "kvp-value.hpp"
36 #include "kvp-frame.hpp"
37 #include <typeinfo>
38 #include <sstream>
39 #include <algorithm>
40 #include <vector>
41 #include <numeric>
42 
43 /* This static indicates the debugging module that this .o belongs to. */
44 static QofLogModule log_module = "qof.kvp";
45 
46 static const char delim = '/';
47 
48 KvpFrameImpl::KvpFrameImpl(const KvpFrameImpl & rhs) noexcept
49 {
50  std::for_each(rhs.m_valuemap.begin(), rhs.m_valuemap.end(),
51  [this](const map_type::value_type & a)
52  {
53  auto key = qof_string_cache_insert(a.first);
54  auto val = new KvpValueImpl(*a.second);
55  this->m_valuemap.insert({key,val});
56  }
57  );
58 }
59 
61 {
62  std::for_each(m_valuemap.begin(), m_valuemap.end(),
63  [](const map_type::value_type &a){
64  qof_string_cache_remove(a.first);
65  delete a.second;
66  }
67  );
68  m_valuemap.clear();
69 }
70 
71 KvpFrame *
72 KvpFrame::get_child_frame_or_nullptr (Path const & path) noexcept
73 {
74  if (!path.size ())
75  return this;
76  auto key = path.front ();
77  if (m_valuemap.find (key.c_str ()) == m_valuemap.end ())
78  return nullptr;
79  auto child = m_valuemap.at (key.c_str ())->get <KvpFrame *> ();
80  Path send;
81  std::copy (path.begin () + 1, path.end (), std::back_inserter (send));
82  return child->get_child_frame_or_nullptr (send);
83 }
84 
85 KvpFrame *
86 KvpFrame::get_child_frame_or_create (Path const & path) noexcept
87 {
88  if (!path.size ())
89  return this;
90  auto key = path.front ();
91  auto spot = m_valuemap.find (key.c_str ());
92  if (spot == m_valuemap.end () || spot->second->get_type () != KvpValue::Type::FRAME)
93  delete set_impl (key.c_str (), new KvpValue {new KvpFrame});
94  Path send;
95  std::copy (path.begin () + 1, path.end (), std::back_inserter (send));
96  auto child_val = m_valuemap.at (key.c_str ());
97  auto child = child_val->get <KvpFrame *> ();
98  return child->get_child_frame_or_create (send);
99 }
100 
101 
102 KvpValue *
103 KvpFrame::set_impl (std::string const & key, KvpValue * value) noexcept
104 {
105  KvpValue * ret {};
106  auto spot = m_valuemap.find (key.c_str ());
107  if (spot != m_valuemap.end ())
108  {
109  qof_string_cache_remove (spot->first);
110  ret = spot->second;
111  m_valuemap.erase (spot);
112  }
113  if (value)
114  {
115  auto cachedkey = static_cast <char const *> (qof_string_cache_insert (key.c_str ()));
116  m_valuemap.emplace (cachedkey, value);
117  }
118  return ret;
119 }
120 
121 KvpValue *
122 KvpFrameImpl::set (Path path, KvpValue* value) noexcept
123 {
124  if (path.empty())
125  return nullptr;
126  auto key = path.back ();
127  path.pop_back ();
128  auto target = get_child_frame_or_nullptr (path);
129  if (!target)
130  return nullptr;
131  return target->set_impl (key, value);
132 }
133 
134 KvpValue *
135 KvpFrameImpl::set_path (Path path, KvpValue* value) noexcept
136 {
137  auto key = path.back();
138  path.pop_back();
139  auto target = get_child_frame_or_create (path);
140  if (!target)
141  return nullptr;
142  return target->set_impl (key, value);
143 }
144 
145 KvpValue *
146 KvpFrameImpl::get_slot (Path path) noexcept
147 {
148  auto key = path.back();
149  path.pop_back();
150  auto target = get_child_frame_or_nullptr (path);
151  if (!target)
152  return nullptr;
153  auto spot = target->m_valuemap.find (key.c_str ());
154  if (spot != target->m_valuemap.end ())
155  return spot->second;
156  return nullptr;
157 }
158 
159 std::string
160 KvpFrameImpl::to_string() const noexcept
161 {
162  return to_string("");
163 }
164 
165 std::string
166 KvpFrameImpl::to_string(std::string const & prefix) const noexcept
167 {
168  if (!m_valuemap.size())
169  return prefix;
170  std::ostringstream ret;
171  std::for_each(m_valuemap.begin(), m_valuemap.end(),
172  [this,&ret,&prefix](const map_type::value_type &a)
173  {
174  std::string new_prefix {prefix};
175  if (a.first)
176  {
177  new_prefix += a.first;
178  new_prefix += "/";
179  }
180  if (a.second)
181  ret << a.second->to_string(new_prefix) << "\n";
182  else
183  ret << new_prefix << "(null)\n";
184  }
185  );
186  return ret.str();
187 }
188 
189 std::vector<std::string>
190 KvpFrameImpl::get_keys() const noexcept
191 {
192  std::vector<std::string> ret;
193  std::for_each(m_valuemap.begin(), m_valuemap.end(),
194  [&ret](const KvpFrameImpl::map_type::value_type &a)
195  {
196  ret.push_back(a.first);
197  }
198  );
199  return ret;
200 }
201 
202 int compare(const KvpFrameImpl * one, const KvpFrameImpl * two) noexcept
203 {
204  if (one && !two) return 1;
205  if (!one && two) return -1;
206  if (!one && !two) return 0;
207  return compare(*one, *two);
208 }
209 
217 int compare(const KvpFrameImpl & one, const KvpFrameImpl & two) noexcept
218 {
219  for (const auto & a : one.m_valuemap)
220  {
221  auto otherspot = two.m_valuemap.find(a.first);
222  if (otherspot == two.m_valuemap.end())
223  {
224  return 1;
225  }
226  auto comparison = compare(a.second,otherspot->second);
227 
228  if (comparison != 0)
229  return comparison;
230  }
231 
232  if (one.m_valuemap.size() < two.m_valuemap.size())
233  return -1;
234  return 0;
235 }
236 
237 
238 static void
239 gvalue_list_from_kvp_value (KvpValue *kval, gpointer pList)
240 {
241  GList **gvlist = NULL;
242  GValue *gval = gvalue_from_kvp_value (kval);
243  gvlist = (GList**)pList;
244  if (G_VALUE_TYPE (gval))
245  *gvlist = g_list_prepend (*gvlist, gval);
246 }
247 
248 static void
249 kvp_value_list_from_gvalue (GValue *gval, gpointer pList)
250 {
251  GList **kvplist = (GList**)pList;
252  KvpValue *kvp;
253  if (!(gval && G_VALUE_TYPE (gval)))
254  return;
255  kvp = kvp_value_from_gvalue (gval);
256  *kvplist = g_list_prepend (*kvplist, kvp);
257 }
258 
259 GValue*
260 gvalue_from_kvp_value (const KvpValue *kval)
261 {
262  GValue *val;
263  gnc_numeric num;
264  Time64 tm;
265  GDate gdate;
266 
267  if (kval == NULL) return NULL;
268  val = g_slice_new0 (GValue);
269 
270  switch (kval->get_type())
271  {
272  case KvpValue::Type::INT64:
273  g_value_init (val, G_TYPE_INT64);
274  g_value_set_int64 (val, kval->get<int64_t>());
275  break;
276  case KvpValue::Type::DOUBLE:
277  g_value_init (val, G_TYPE_DOUBLE);
278  g_value_set_double (val, kval->get<double>());
279  break;
280  case KvpValue::Type::NUMERIC:
281  g_value_init (val, GNC_TYPE_NUMERIC);
282  num = kval->get<gnc_numeric>();
283  g_value_set_boxed (val, &num);
284  break;
285  case KvpValue::Type::STRING:
286  g_value_init (val, G_TYPE_STRING);
287  g_value_set_string (val, kval->get<const char*>());
288  break;
289  case KvpValue::Type::GUID:
290  g_value_init (val, GNC_TYPE_GUID);
291  g_value_set_boxed (val, kval->get<GncGUID*>());
292  break;
293  case KvpValue::Type::TIME64:
294  g_value_init (val, GNC_TYPE_TIME64);
295  tm = kval->get<Time64>();
296  g_value_set_boxed (val, &tm);
297  break;
298  case KvpValue::Type::GDATE:
299  g_value_init (val, G_TYPE_DATE);
300  gdate = kval->get<GDate>();
301  g_value_set_boxed (val, &gdate);
302  break;
303  case KvpValue::Type::GLIST:
304  {
305  GList *gvalue_list = NULL;
306  GList *kvp_list = kval->get<GList*>();
307  g_list_foreach (kvp_list, (GFunc)gvalue_list_from_kvp_value,
308  &gvalue_list);
309  g_value_init (val, GNC_TYPE_VALUE_LIST);
310  gvalue_list = g_list_reverse (gvalue_list);
311  g_value_set_boxed (val, gvalue_list);
312  break;
313  }
314 /* No transfer of KVP frames outside of QofInstance-derived classes! */
315  case KvpValue::Type::FRAME:
316  PWARN ("Error! Attempt to transfer KvpFrame!");
317  default:
318  PWARN ("Error! Invalid KVP Transfer Request!");
319  g_slice_free (GValue, val);
320  val = NULL;
321  break;
322  }
323  return val;
324 }
325 
326 KvpValue*
327 kvp_value_from_gvalue (const GValue *gval)
328 {
329  KvpValue *val = NULL;
330  GType type;
331  if (gval == NULL)
332  return NULL;
333  type = G_VALUE_TYPE (gval);
334  g_return_val_if_fail (G_VALUE_TYPE (gval), NULL);
335 
336  if (type == G_TYPE_INT64)
337  val = new KvpValue(g_value_get_int64 (gval));
338  else if (type == G_TYPE_DOUBLE)
339  val = new KvpValue(g_value_get_double (gval));
340  else if (type == G_TYPE_BOOLEAN)
341  {
342  auto bval = g_value_get_boolean(gval);
343  if (bval)
344  val = new KvpValue(g_strdup("true"));
345  }
346  else if (type == GNC_TYPE_NUMERIC)
347  val = new KvpValue(*(gnc_numeric*)g_value_get_boxed (gval));
348  else if (type == G_TYPE_STRING)
349  {
350  auto string = g_value_get_string(gval);
351  if (string != nullptr)
352  val = new KvpValue(g_strdup(string));
353  }
354  else if (type == GNC_TYPE_GUID)
355  {
356  auto boxed = g_value_get_boxed(gval);
357  if (boxed != nullptr)
358  val = new KvpValue(guid_copy(static_cast<GncGUID*>(boxed)));
359  }
360  else if (type == GNC_TYPE_TIME64)
361  val = new KvpValue(*(Time64*)g_value_get_boxed (gval));
362  else if (type == G_TYPE_DATE)
363  val = new KvpValue(*(GDate*)g_value_get_boxed (gval));
364  else if (type == GNC_TYPE_VALUE_LIST)
365  {
366  GList *gvalue_list = (GList*)g_value_get_boxed (gval);
367  GList *kvp_list = NULL;
368  g_list_foreach (gvalue_list, (GFunc)kvp_value_list_from_gvalue,
369  &kvp_list);
370  kvp_list = g_list_reverse (kvp_list);
371  val = new KvpValue(kvp_list);
372 // g_list_free_full (gvalue_list, (GDestroyNotify)g_value_unset);
373 // gvalue_list = NULL;
374  }
375  else
376  PWARN ("Error! Don't know how to make a KvpValue from a %s",
377  G_VALUE_TYPE_NAME (gval));
378 
379  return val;
380 }
381 /* The following are required for using KvpValue GLists as GValues */
382 static void
383 gnc_gvalue_copy (GValue *src, gpointer uData)
384 {
385  GList **new_list = (GList**)uData;
386  GValue *dest = g_value_init (g_slice_new0 (GValue), G_VALUE_TYPE (src));
387  g_value_copy (src, dest);
388  *new_list = g_list_prepend(*new_list, dest);
389 }
390 
391 void
392 gnc_gvalue_free (GValue *val)
393 {
394  if (val == NULL || ! G_IS_VALUE (val)) return;
395  g_value_unset (val);
396  g_slice_free (GValue, val);
397 }
398 
399 static GList*
400 gnc_value_list_copy (GList *list)
401 {
402  GList *new_list = NULL;
403  g_list_foreach (list, (GFunc)gnc_gvalue_copy, &new_list);
404  new_list = g_list_reverse (new_list);
405  return new_list;
406 }
407 
408 static void
409 gnc_value_list_free (GList *list)
410 {
411  g_list_free_full (list, (GDestroyNotify)gnc_gvalue_free);
412 }
413 
414 GType
415 gnc_value_list_get_type (void)
416 {
417  static GType type = 0;
418  if (type == 0)
419  {
420  type = g_boxed_type_register_static ("gnc_value_list",
421  (GBoxedCopyFunc)gnc_value_list_copy,
422  (GBoxedFreeFunc)gnc_value_list_free);
423  }
424  return type;
425 }
426 
427 void
428 KvpFrame::flatten_kvp_impl(std::vector <std::string> path, std::vector <KvpEntry> & entries) const noexcept
429 {
430  for (auto const & entry : m_valuemap)
431  {
432  std::vector<std::string> new_path {path};
433  new_path.push_back("/");
434  if (entry.second->get_type() == KvpValue::Type::FRAME)
435  {
436  new_path.push_back(entry.first);
437  entry.second->get<KvpFrame*>()->flatten_kvp_impl(new_path, entries);
438  }
439  else
440  {
441  new_path.emplace_back (entry.first);
442  entries.emplace_back (new_path, entry.second);
443  }
444  }
445 }
446 
447 std::vector <KvpEntry>
448 KvpFrame::flatten_kvp(void) const noexcept
449 {
450  std::vector <KvpEntry> ret;
451  flatten_kvp_impl({}, ret);
452  return ret;
453 }
~KvpFrameImpl() noexcept
Perform a deep delete.
Definition: kvp-frame.cpp:60
Implements KvpFrame.
Definition: kvp-frame.hpp:109
GncGUID * guid_copy(const GncGUID *guid)
Returns a newly allocated GncGUID that matches the passed-in GUID.
Definition: guid.cpp:121
std::string to_string() const noexcept
Make a string representation of the frame.
Definition: kvp-frame.cpp:160
KvpValue * kvp_value_from_gvalue(const GValue *gval)
Convert a gvalue into a kvpvalue.
Definition: kvp-frame.cpp:327
KvpValue * set(Path path, KvpValue *newvalue) noexcept
Set the value with the key in the immediate frame, replacing and returning the old value if it exists...
Definition: kvp-frame.cpp:122
const char * qof_string_cache_insert(const char *key)
You can use this function with g_hash_table_insert(), for the key (or value), as long as you use the ...
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
void gnc_gvalue_free(GValue *value)
Convenience function to release the value in a GValue acquired by kvp_frame_get_gvalue and to free th...
Definition: kvp-frame.cpp:392
int compare(const KvpFrameImpl &one, const KvpFrameImpl &two) noexcept
If the first KvpFrameImpl has an item that the second does not, 1 is returned.
Definition: kvp-frame.cpp:217
std::vector< std::string > get_keys() const noexcept
Report the keys in the immediate frame.
Definition: kvp-frame.cpp:190
Implements KvpValue using boost::variant.
Definition: kvp-value.hpp:57
KvpValue * get_slot(Path keys) noexcept
Get the value for the tail of the path or nullptr if it doesn&#39;t exist.
Definition: kvp-frame.cpp:146
GValue * gvalue_from_kvp_value(const KvpValue *kval)
Convert a kvp_value into a GValue.
Definition: kvp-frame.cpp:260
KvpValue * set_path(Path path, KvpValue *newvalue) noexcept
Set the value with the key in a subframe following the keys in path, replacing and returning the old ...
Definition: kvp-frame.cpp:135
The type used to store guids in C.
Definition: guid.h:75
void qof_string_cache_remove(const char *key)
You can use this function as a destroy notifier for a GHashTable that uses common strings as keys (or...