GnuCash  4.12-404-geb24099a91
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  auto map_iter = m_valuemap.find (key.c_str ());
78  if (map_iter == m_valuemap.end ())
79  return nullptr;
80  auto child = map_iter->second->get <KvpFrame *> ();
81  if (!child)
82  return nullptr;
83  Path send;
84  std::copy (path.begin () + 1, path.end (), std::back_inserter (send));
85  return child->get_child_frame_or_nullptr (send);
86 }
87 
88 KvpFrame *
89 KvpFrame::get_child_frame_or_create (Path const & path) noexcept
90 {
91  if (!path.size ())
92  return this;
93  auto key = path.front ();
94  auto spot = m_valuemap.find (key.c_str ());
95  if (spot == m_valuemap.end () || spot->second->get_type () != KvpValue::Type::FRAME)
96  delete set_impl (key.c_str (), new KvpValue {new KvpFrame});
97  Path send;
98  std::copy (path.begin () + 1, path.end (), std::back_inserter (send));
99  auto child_val = m_valuemap.at (key.c_str ());
100  auto child = child_val->get <KvpFrame *> ();
101  return child->get_child_frame_or_create (send);
102 }
103 
104 
105 KvpValue *
106 KvpFrame::set_impl (std::string const & key, KvpValue * value) noexcept
107 {
108  KvpValue * ret {};
109  auto spot = m_valuemap.find (key.c_str ());
110  if (spot != m_valuemap.end ())
111  {
112  qof_string_cache_remove (spot->first);
113  ret = spot->second;
114  m_valuemap.erase (spot);
115  }
116  if (value)
117  {
118  auto cachedkey = static_cast <char const *> (qof_string_cache_insert (key.c_str ()));
119  m_valuemap.emplace (cachedkey, value);
120  }
121  return ret;
122 }
123 
124 KvpValue *
125 KvpFrameImpl::set (Path path, KvpValue* value) noexcept
126 {
127  if (path.empty())
128  return nullptr;
129  auto key = path.back ();
130  path.pop_back ();
131  auto target = get_child_frame_or_nullptr (path);
132  if (!target)
133  return nullptr;
134  return target->set_impl (key, value);
135 }
136 
137 KvpValue *
138 KvpFrameImpl::set_path (Path path, KvpValue* value) noexcept
139 {
140  auto key = path.back();
141  path.pop_back();
142  auto target = get_child_frame_or_create (path);
143  if (!target)
144  return nullptr;
145  return target->set_impl (key, value);
146 }
147 
148 KvpValue *
149 KvpFrameImpl::get_slot (Path path) noexcept
150 {
151  auto key = path.back();
152  path.pop_back();
153  auto target = get_child_frame_or_nullptr (path);
154  if (!target)
155  return nullptr;
156  auto spot = target->m_valuemap.find (key.c_str ());
157  if (spot != target->m_valuemap.end ())
158  return spot->second;
159  return nullptr;
160 }
161 
162 std::string
163 KvpFrameImpl::to_string() const noexcept
164 {
165  return to_string("");
166 }
167 
168 std::string
169 KvpFrameImpl::to_string(std::string const & prefix) const noexcept
170 {
171  if (!m_valuemap.size())
172  return prefix;
173  std::ostringstream ret;
174  std::for_each(m_valuemap.begin(), m_valuemap.end(),
175  [this,&ret,&prefix](const map_type::value_type &a)
176  {
177  std::string new_prefix {prefix};
178  if (a.first)
179  {
180  new_prefix += a.first;
181  new_prefix += "/";
182  }
183  if (a.second)
184  ret << a.second->to_string(new_prefix) << "\n";
185  else
186  ret << new_prefix << "(null)\n";
187  }
188  );
189  return ret.str();
190 }
191 
192 std::vector<std::string>
193 KvpFrameImpl::get_keys() const noexcept
194 {
195  std::vector<std::string> ret;
196  ret.reserve (m_valuemap.size());
197  std::for_each(m_valuemap.begin(), m_valuemap.end(),
198  [&ret](const KvpFrameImpl::map_type::value_type &a)
199  {
200  ret.push_back(a.first);
201  }
202  );
203  return ret;
204 }
205 
206 int compare(const KvpFrameImpl * one, const KvpFrameImpl * two) noexcept
207 {
208  if (one && !two) return 1;
209  if (!one && two) return -1;
210  if (!one && !two) return 0;
211  return compare(*one, *two);
212 }
213 
221 int compare(const KvpFrameImpl & one, const KvpFrameImpl & two) noexcept
222 {
223  for (const auto & a : one.m_valuemap)
224  {
225  auto otherspot = two.m_valuemap.find(a.first);
226  if (otherspot == two.m_valuemap.end())
227  {
228  return 1;
229  }
230  auto comparison = compare(a.second,otherspot->second);
231 
232  if (comparison != 0)
233  return comparison;
234  }
235 
236  if (one.m_valuemap.size() < two.m_valuemap.size())
237  return -1;
238  return 0;
239 }
240 
241 
242 static void
243 gvalue_list_from_kvp_value (KvpValue *kval, gpointer pList)
244 {
245  GList **gvlist = NULL;
246  GValue *gval = gvalue_from_kvp_value (kval);
247  gvlist = (GList**)pList;
248  if (G_VALUE_TYPE (gval))
249  *gvlist = g_list_prepend (*gvlist, gval);
250 }
251 
252 static void
253 kvp_value_list_from_gvalue (GValue *gval, gpointer pList)
254 {
255  GList **kvplist = (GList**)pList;
256  KvpValue *kvp;
257  if (!(gval && G_VALUE_TYPE (gval)))
258  return;
259  kvp = kvp_value_from_gvalue (gval);
260  *kvplist = g_list_prepend (*kvplist, kvp);
261 }
262 
263 GValue*
264 gvalue_from_kvp_value (const KvpValue *kval)
265 {
266  GValue *val;
267  gnc_numeric num;
268  Time64 tm;
269  GDate gdate;
270 
271  if (kval == NULL) return NULL;
272  val = g_slice_new0 (GValue);
273 
274  switch (kval->get_type())
275  {
276  case KvpValue::Type::INT64:
277  g_value_init (val, G_TYPE_INT64);
278  g_value_set_int64 (val, kval->get<int64_t>());
279  break;
280  case KvpValue::Type::DOUBLE:
281  g_value_init (val, G_TYPE_DOUBLE);
282  g_value_set_double (val, kval->get<double>());
283  break;
284  case KvpValue::Type::NUMERIC:
285  g_value_init (val, GNC_TYPE_NUMERIC);
286  num = kval->get<gnc_numeric>();
287  g_value_set_boxed (val, &num);
288  break;
289  case KvpValue::Type::STRING:
290  g_value_init (val, G_TYPE_STRING);
291  g_value_set_string (val, kval->get<const char*>());
292  break;
293  case KvpValue::Type::GUID:
294  g_value_init (val, GNC_TYPE_GUID);
295  g_value_set_boxed (val, kval->get<GncGUID*>());
296  break;
297  case KvpValue::Type::TIME64:
298  g_value_init (val, GNC_TYPE_TIME64);
299  tm = kval->get<Time64>();
300  g_value_set_boxed (val, &tm);
301  break;
302  case KvpValue::Type::GDATE:
303  g_value_init (val, G_TYPE_DATE);
304  gdate = kval->get<GDate>();
305  g_value_set_boxed (val, &gdate);
306  break;
307  case KvpValue::Type::GLIST:
308  {
309  GList *gvalue_list = NULL;
310  GList *kvp_list = kval->get<GList*>();
311  g_list_foreach (kvp_list, (GFunc)gvalue_list_from_kvp_value,
312  &gvalue_list);
313  g_value_init (val, GNC_TYPE_VALUE_LIST);
314  gvalue_list = g_list_reverse (gvalue_list);
315  g_value_set_boxed (val, gvalue_list);
316  break;
317  }
318 /* No transfer of KVP frames outside of QofInstance-derived classes! */
319  case KvpValue::Type::FRAME:
320  PWARN ("Error! Attempt to transfer KvpFrame!");
321  default:
322  PWARN ("Error! Invalid KVP Transfer Request!");
323  g_slice_free (GValue, val);
324  val = NULL;
325  break;
326  }
327  return val;
328 }
329 
330 KvpValue*
331 kvp_value_from_gvalue (const GValue *gval)
332 {
333  KvpValue *val = NULL;
334  GType type;
335  if (gval == NULL)
336  return NULL;
337  type = G_VALUE_TYPE (gval);
338  g_return_val_if_fail (G_VALUE_TYPE (gval), NULL);
339 
340  if (type == G_TYPE_INT64)
341  val = new KvpValue(g_value_get_int64 (gval));
342  else if (type == G_TYPE_DOUBLE)
343  val = new KvpValue(g_value_get_double (gval));
344  else if (type == G_TYPE_BOOLEAN)
345  {
346  auto bval = g_value_get_boolean(gval);
347  if (bval)
348  val = new KvpValue(g_strdup("true"));
349  }
350  else if (type == GNC_TYPE_NUMERIC)
351  val = new KvpValue(*(gnc_numeric*)g_value_get_boxed (gval));
352  else if (type == G_TYPE_STRING)
353  {
354  auto string = g_value_get_string(gval);
355  if (string != nullptr)
356  val = new KvpValue(g_strdup(string));
357  }
358  else if (type == GNC_TYPE_GUID)
359  {
360  auto boxed = g_value_get_boxed(gval);
361  if (boxed != nullptr)
362  val = new KvpValue(guid_copy(static_cast<GncGUID*>(boxed)));
363  }
364  else if (type == GNC_TYPE_TIME64)
365  val = new KvpValue(*(Time64*)g_value_get_boxed (gval));
366  else if (type == G_TYPE_DATE)
367  val = new KvpValue(*(GDate*)g_value_get_boxed (gval));
368  else if (type == GNC_TYPE_VALUE_LIST)
369  {
370  GList *gvalue_list = (GList*)g_value_get_boxed (gval);
371  GList *kvp_list = NULL;
372  g_list_foreach (gvalue_list, (GFunc)kvp_value_list_from_gvalue,
373  &kvp_list);
374  kvp_list = g_list_reverse (kvp_list);
375  val = new KvpValue(kvp_list);
376 // g_list_free_full (gvalue_list, (GDestroyNotify)g_value_unset);
377 // gvalue_list = NULL;
378  }
379  else
380  PWARN ("Error! Don't know how to make a KvpValue from a %s",
381  G_VALUE_TYPE_NAME (gval));
382 
383  return val;
384 }
385 /* The following are required for using KvpValue GLists as GValues */
386 static void
387 gnc_gvalue_copy (GValue *src, gpointer uData)
388 {
389  GList **new_list = (GList**)uData;
390  GValue *dest = g_value_init (g_slice_new0 (GValue), G_VALUE_TYPE (src));
391  g_value_copy (src, dest);
392  *new_list = g_list_prepend(*new_list, dest);
393 }
394 
395 void
396 gnc_gvalue_free (GValue *val)
397 {
398  if (val == NULL || ! G_IS_VALUE (val)) return;
399  g_value_unset (val);
400  g_slice_free (GValue, val);
401 }
402 
403 static GList*
404 gnc_value_list_copy (GList *list)
405 {
406  GList *new_list = NULL;
407  g_list_foreach (list, (GFunc)gnc_gvalue_copy, &new_list);
408  new_list = g_list_reverse (new_list);
409  return new_list;
410 }
411 
412 static void
413 gnc_value_list_free (GList *list)
414 {
415  g_list_free_full (list, (GDestroyNotify)gnc_gvalue_free);
416 }
417 
418 GType
419 gnc_value_list_get_type (void)
420 {
421  static GType type = 0;
422  if (type == 0)
423  {
424  type = g_boxed_type_register_static ("gnc_value_list",
425  (GBoxedCopyFunc)gnc_value_list_copy,
426  (GBoxedFreeFunc)gnc_value_list_free);
427  }
428  return type;
429 }
430 
431 void
432 KvpFrame::flatten_kvp_impl(std::vector <std::string> path, std::vector <KvpEntry> & entries) const noexcept
433 {
434  for (auto const & entry : m_valuemap)
435  {
436  std::vector<std::string> new_path {path};
437  new_path.push_back("/");
438  if (entry.second->get_type() == KvpValue::Type::FRAME)
439  {
440  new_path.push_back(entry.first);
441  entry.second->get<KvpFrame*>()->flatten_kvp_impl(new_path, entries);
442  }
443  else
444  {
445  new_path.emplace_back (entry.first);
446  entries.emplace_back (new_path, entry.second);
447  }
448  }
449 }
450 
451 std::vector <KvpEntry>
452 KvpFrame::flatten_kvp(void) const noexcept
453 {
454  std::vector <KvpEntry> ret;
455  flatten_kvp_impl({}, ret);
456  return ret;
457 }
~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:163
KvpValue * kvp_value_from_gvalue(const GValue *gval)
Convert a gvalue into a kvpvalue.
Definition: kvp-frame.cpp:331
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:125
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:396
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:221
std::vector< std::string > get_keys() const noexcept
Report the keys in the immediate frame.
Definition: kvp-frame.cpp:193
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:149
GValue * gvalue_from_kvp_value(const KvpValue *kval)
Convert a kvp_value into a GValue.
Definition: kvp-frame.cpp:264
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:138
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...