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