GnuCash  5.6-150-g038405b370+
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 #include <config.h>
25 #include "qof.h"
26 #include <glib.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <string.h>
30 
31 #include "kvp-value.hpp"
32 #include "kvp-frame.hpp"
33 #include <typeinfo>
34 #include <sstream>
35 #include <algorithm>
36 #include <vector>
37 #include <numeric>
38 
39 /* This static indicates the debugging module that this .o belongs to. */
40 static QofLogModule log_module = "qof.kvp";
41 
42 KvpFrameImpl::KvpFrameImpl(const KvpFrameImpl & rhs) noexcept
43 {
44  std::for_each(rhs.m_valuemap.begin(), rhs.m_valuemap.end(),
45  [this](const map_type::value_type & a)
46  {
47  auto key = qof_string_cache_insert(a.first);
48  auto val = new KvpValueImpl(*a.second);
49  this->m_valuemap.insert({key,val});
50  }
51  );
52 }
53 
55 {
56  std::for_each(m_valuemap.begin(), m_valuemap.end(),
57  [](const map_type::value_type &a){
58  qof_string_cache_remove(a.first);
59  delete a.second;
60  }
61  );
62  m_valuemap.clear();
63 }
64 
65 KvpFrame *
66 KvpFrame::get_child_frame_or_nullptr (Path const & path) noexcept
67 {
68  if (!path.size ())
69  return this;
70  auto key = path.front ();
71  auto map_iter = m_valuemap.find (key.c_str ());
72  if (map_iter == m_valuemap.end ())
73  return nullptr;
74  auto child = map_iter->second->get <KvpFrame *> ();
75  if (!child)
76  return nullptr;
77  Path send;
78  std::copy (path.begin () + 1, path.end (), std::back_inserter (send));
79  return child->get_child_frame_or_nullptr (send);
80 }
81 
82 KvpFrame *
83 KvpFrame::get_child_frame_or_create (Path const & path) noexcept
84 {
85  if (!path.size ())
86  return this;
87  auto key = path.front ();
88  auto spot = m_valuemap.find (key.c_str ());
89  if (spot == m_valuemap.end () || spot->second->get_type () != KvpValue::Type::FRAME)
90  delete set_impl (key.c_str (), new KvpValue {new KvpFrame});
91  Path send;
92  std::copy (path.begin () + 1, path.end (), std::back_inserter (send));
93  auto child_val = m_valuemap.at (key.c_str ());
94  auto child = child_val->get <KvpFrame *> ();
95  return child->get_child_frame_or_create (send);
96 }
97 
98 
99 KvpValue *
100 KvpFrame::set_impl (std::string const & key, KvpValue * value) noexcept
101 {
102  KvpValue * ret {};
103  auto spot = m_valuemap.find (key.c_str ());
104  if (spot != m_valuemap.end ())
105  {
106  qof_string_cache_remove (spot->first);
107  ret = spot->second;
108  m_valuemap.erase (spot);
109  }
110  if (value)
111  {
112  auto cachedkey = static_cast <char const *> (qof_string_cache_insert (key.c_str ()));
113  m_valuemap.emplace (cachedkey, value);
114  }
115  return ret;
116 }
117 
118 KvpValue *
119 KvpFrameImpl::set (Path path, KvpValue* value) noexcept
120 {
121  if (path.empty())
122  return nullptr;
123  auto key = path.back ();
124  path.pop_back ();
125  auto target = get_child_frame_or_nullptr (path);
126  if (!target)
127  return nullptr;
128  return target->set_impl (key, value);
129 }
130 
131 KvpValue *
132 KvpFrameImpl::set_path (Path path, KvpValue* value) noexcept
133 {
134  auto key = path.back();
135  path.pop_back();
136  auto target = get_child_frame_or_create (path);
137  if (!target)
138  return nullptr;
139  return target->set_impl (key, value);
140 }
141 
142 KvpValue *
143 KvpFrameImpl::get_slot (Path path) noexcept
144 {
145  auto key = path.back();
146  path.pop_back();
147  auto target = get_child_frame_or_nullptr (path);
148  if (!target)
149  return nullptr;
150  auto spot = target->m_valuemap.find (key.c_str ());
151  if (spot != target->m_valuemap.end ())
152  return spot->second;
153  return nullptr;
154 }
155 
156 std::string
157 KvpFrameImpl::to_string() const noexcept
158 {
159  return to_string("");
160 }
161 
162 std::string
163 KvpFrameImpl::to_string(std::string const & prefix) const noexcept
164 {
165  if (!m_valuemap.size())
166  return prefix;
167  std::ostringstream ret;
168  std::for_each(m_valuemap.begin(), m_valuemap.end(),
169  [&ret,&prefix](const map_type::value_type &a)
170  {
171  std::string new_prefix {prefix};
172  if (a.first)
173  {
174  new_prefix += a.first;
175  new_prefix += "/";
176  }
177  if (a.second)
178  ret << a.second->to_string(new_prefix) << "\n";
179  else
180  ret << new_prefix << "(null)\n";
181  }
182  );
183  return ret.str();
184 }
185 
186 std::vector<std::string>
187 KvpFrameImpl::get_keys() const noexcept
188 {
189  std::vector<std::string> ret;
190  ret.reserve (m_valuemap.size());
191  std::for_each(m_valuemap.begin(), m_valuemap.end(),
192  [&ret](const KvpFrameImpl::map_type::value_type &a)
193  {
194  ret.push_back(a.first);
195  }
196  );
197  return ret;
198 }
199 
200 int compare(const KvpFrameImpl * one, const KvpFrameImpl * two) noexcept
201 {
202  if (one && !two) return 1;
203  if (!one && two) return -1;
204  if (!one && !two) return 0;
205  return compare(*one, *two);
206 }
207 
215 int compare(const KvpFrameImpl & one, const KvpFrameImpl & two) noexcept
216 {
217  for (const auto & a : one.m_valuemap)
218  {
219  auto otherspot = two.m_valuemap.find(a.first);
220  if (otherspot == two.m_valuemap.end())
221  {
222  return 1;
223  }
224  auto comparison = compare(a.second,otherspot->second);
225 
226  if (comparison != 0)
227  return comparison;
228  }
229 
230  if (one.m_valuemap.size() < two.m_valuemap.size())
231  return -1;
232  return 0;
233 }
234 
235 GValue*
236 gvalue_from_kvp_value (const KvpValue *kval, GValue* val)
237 {
238  if (kval == NULL) return NULL;
239  if (!val)
240  val = g_slice_new0 (GValue);
241  else
242  g_value_unset(val);
243 
244  switch (kval->get_type())
245  {
246  case KvpValue::Type::INT64:
247  g_value_init (val, G_TYPE_INT64);
248  g_value_set_int64 (val, kval->get<int64_t>());
249  break;
250  case KvpValue::Type::DOUBLE:
251  g_value_init (val, G_TYPE_DOUBLE);
252  g_value_set_double (val, kval->get<double>());
253  break;
254  case KvpValue::Type::NUMERIC:
255  g_value_init (val, GNC_TYPE_NUMERIC);
256  g_value_set_static_boxed (val, kval->get_ptr<gnc_numeric>());
257  break;
258  case KvpValue::Type::STRING:
259  g_value_init (val, G_TYPE_STRING);
260  g_value_set_static_string (val, kval->get<const char*>());
261  break;
262  case KvpValue::Type::GUID:
263  g_value_init (val, GNC_TYPE_GUID);
264  g_value_set_static_boxed (val, kval->get<GncGUID*>());
265  break;
266  case KvpValue::Type::TIME64:
267  g_value_init (val, GNC_TYPE_TIME64);
268  g_value_set_boxed (val, kval->get_ptr<Time64>());
269  break;
270  case KvpValue::Type::GDATE:
271  g_value_init (val, G_TYPE_DATE);
272  g_value_set_static_boxed (val, kval->get_ptr<GDate>());
273  break;
274  default:
275 /* No transfer outside of QofInstance-derived classes! */
276  PWARN ("Error! Invalid attempt to transfer Kvp type %d", kval->get_type());
277  g_slice_free (GValue, val);
278  val = NULL;
279  break;
280  }
281  return val;
282 }
283 
284 KvpValue*
285 kvp_value_from_gvalue (const GValue *gval)
286 {
287  KvpValue *val = NULL;
288  GType type;
289  if (gval == NULL)
290  return NULL;
291  type = G_VALUE_TYPE (gval);
292  g_return_val_if_fail (G_VALUE_TYPE (gval), NULL);
293 
294  if (type == G_TYPE_INT64)
295  val = new KvpValue(g_value_get_int64 (gval));
296  else if (type == G_TYPE_DOUBLE)
297  val = new KvpValue(g_value_get_double (gval));
298  else if (type == G_TYPE_BOOLEAN)
299  {
300  auto bval = g_value_get_boolean(gval);
301  if (bval)
302  val = new KvpValue(g_strdup("true"));
303  }
304  else if (type == GNC_TYPE_NUMERIC)
305  val = new KvpValue(*(gnc_numeric*)g_value_get_boxed (gval));
306  else if (type == G_TYPE_STRING)
307  {
308  auto string = g_value_get_string(gval);
309  if (string != nullptr)
310  val = new KvpValue(g_strdup(string));
311  }
312  else if (type == GNC_TYPE_GUID)
313  {
314  auto boxed = g_value_get_boxed(gval);
315  if (boxed != nullptr)
316  val = new KvpValue(guid_copy(static_cast<GncGUID*>(boxed)));
317  }
318  else if (type == GNC_TYPE_TIME64)
319  val = new KvpValue(*(Time64*)g_value_get_boxed (gval));
320  else if (type == G_TYPE_DATE)
321  val = new KvpValue(*(GDate*)g_value_get_boxed (gval));
322  else
323  PWARN ("Error! Don't know how to make a KvpValue from a %s",
324  G_VALUE_TYPE_NAME (gval));
325 
326  return val;
327 }
328 
329 void
330 KvpFrame::flatten_kvp_impl(std::vector <std::string> path, std::vector <KvpEntry> & entries) const noexcept
331 {
332  for (auto const & entry : m_valuemap)
333  {
334  std::vector<std::string> new_path {path};
335  new_path.push_back("/");
336  if (entry.second->get_type() == KvpValue::Type::FRAME)
337  {
338  new_path.push_back(entry.first);
339  entry.second->get<KvpFrame*>()->flatten_kvp_impl(new_path, entries);
340  }
341  else
342  {
343  new_path.emplace_back (entry.first);
344  entries.emplace_back (new_path, entry.second);
345  }
346  }
347 }
348 
349 std::vector <KvpEntry>
350 KvpFrame::flatten_kvp(void) const noexcept
351 {
352  std::vector <KvpEntry> ret;
353  flatten_kvp_impl({}, ret);
354  return ret;
355 }
~KvpFrameImpl() noexcept
Perform a deep delete.
Definition: kvp-frame.cpp:54
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:120
std::string to_string() const noexcept
Make a string representation of the frame.
Definition: kvp-frame.cpp:157
KvpValue * kvp_value_from_gvalue(const GValue *gval)
Convert a gvalue into a kvpvalue.
Definition: kvp-frame.cpp:285
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:119
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
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:215
std::vector< std::string > get_keys() const noexcept
Report the keys in the immediate frame.
Definition: kvp-frame.cpp:187
Implements KvpValue using boost::variant.
Definition: kvp-value.hpp:51
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:143
GValue * gvalue_from_kvp_value(const KvpValue *kval, GValue *val)
Convert a kvp_value into a GValue.
Definition: kvp-frame.cpp:236
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:132
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...