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 void
236 gvalue_from_kvp_value (const KvpValue *kval, GValue* val)
237 {
238  if (kval == NULL) return;
239  g_value_unset(val);
240 
241  switch (kval->get_type())
242  {
243  case KvpValue::Type::INT64:
244  g_value_init (val, G_TYPE_INT64);
245  g_value_set_int64 (val, kval->get<int64_t>());
246  break;
247  case KvpValue::Type::DOUBLE:
248  g_value_init (val, G_TYPE_DOUBLE);
249  g_value_set_double (val, kval->get<double>());
250  break;
251  case KvpValue::Type::NUMERIC:
252  g_value_init (val, GNC_TYPE_NUMERIC);
253  g_value_set_static_boxed (val, kval->get_ptr<gnc_numeric>());
254  break;
255  case KvpValue::Type::STRING:
256  g_value_init (val, G_TYPE_STRING);
257  g_value_set_static_string (val, kval->get<const char*>());
258  break;
259  case KvpValue::Type::GUID:
260  g_value_init (val, GNC_TYPE_GUID);
261  g_value_set_static_boxed (val, kval->get<GncGUID*>());
262  break;
263  case KvpValue::Type::TIME64:
264  g_value_init (val, GNC_TYPE_TIME64);
265  g_value_set_boxed (val, kval->get_ptr<Time64>());
266  break;
267  case KvpValue::Type::GDATE:
268  g_value_init (val, G_TYPE_DATE);
269  g_value_set_static_boxed (val, kval->get_ptr<GDate>());
270  break;
271  default:
272 /* No transfer outside of QofInstance-derived classes! */
273  PWARN ("Error! Invalid attempt to transfer Kvp type %d", kval->get_type());
274  break;
275  }
276 }
277 
278 KvpValue*
279 kvp_value_from_gvalue (const GValue *gval)
280 {
281  KvpValue *val = NULL;
282  GType type;
283  if (gval == NULL)
284  return NULL;
285  type = G_VALUE_TYPE (gval);
286  g_return_val_if_fail (G_VALUE_TYPE (gval), NULL);
287 
288  if (type == G_TYPE_INT64)
289  val = new KvpValue(g_value_get_int64 (gval));
290  else if (type == G_TYPE_DOUBLE)
291  val = new KvpValue(g_value_get_double (gval));
292  else if (type == G_TYPE_BOOLEAN)
293  {
294  auto bval = g_value_get_boolean(gval);
295  if (bval)
296  val = new KvpValue(g_strdup("true"));
297  }
298  else if (type == GNC_TYPE_NUMERIC)
299  val = new KvpValue(*(gnc_numeric*)g_value_get_boxed (gval));
300  else if (type == G_TYPE_STRING)
301  {
302  auto string = g_value_get_string(gval);
303  if (string != nullptr)
304  val = new KvpValue(g_strdup(string));
305  }
306  else if (type == GNC_TYPE_GUID)
307  {
308  auto boxed = g_value_get_boxed(gval);
309  if (boxed != nullptr)
310  val = new KvpValue(guid_copy(static_cast<GncGUID*>(boxed)));
311  }
312  else if (type == GNC_TYPE_TIME64)
313  val = new KvpValue(*(Time64*)g_value_get_boxed (gval));
314  else if (type == G_TYPE_DATE)
315  val = new KvpValue(*(GDate*)g_value_get_boxed (gval));
316  else
317  PWARN ("Error! Don't know how to make a KvpValue from a %s",
318  G_VALUE_TYPE_NAME (gval));
319 
320  return val;
321 }
322 
323 void
324 KvpFrame::flatten_kvp_impl(std::vector <std::string> path, std::vector <KvpEntry> & entries) const noexcept
325 {
326  for (auto const & entry : m_valuemap)
327  {
328  std::vector<std::string> new_path {path};
329  new_path.push_back("/");
330  if (entry.second->get_type() == KvpValue::Type::FRAME)
331  {
332  new_path.push_back(entry.first);
333  entry.second->get<KvpFrame*>()->flatten_kvp_impl(new_path, entries);
334  }
335  else
336  {
337  new_path.emplace_back (entry.first);
338  entries.emplace_back (new_path, entry.second);
339  }
340  }
341 }
342 
343 std::vector <KvpEntry>
344 KvpFrame::flatten_kvp(void) const noexcept
345 {
346  std::vector <KvpEntry> ret;
347  flatten_kvp_impl({}, ret);
348  return ret;
349 }
~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
void gvalue_from_kvp_value(const KvpValue *kval, GValue *val)
Convert a kvp_value into a GValue.
Definition: kvp-frame.cpp:236
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:279
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
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...