GnuCash  4.8a-176-g88ecf8dd1
kvp-value.cpp
1 /********************************************************************\
2  * kvp-value.cpp -- Implements a key-value frame system *
3  * Copyright (C) 2014 Aaron Laws *
4  * *
5  * This program is free software; you can redistribute it and/or *
6  * modify it under the terms of the GNU General Public License as *
7  * published by the Free Software Foundation; either version 2 of *
8  * the License, or (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License*
16  * along with this program; if not, contact: *
17  * *
18  * Free Software Foundation Voice: +1-617-542-5942 *
19  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
20  * Boston, MA 02110-1301, USA gnu@gnu.org *
21  * *
22 \********************************************************************/
23 
24 #include "kvp-value.hpp"
25 #include "kvp-frame.hpp"
26 #include <cmath>
27 
28 #include <sstream>
29 #include <iomanip>
30 #include <stdexcept>
31 
33 {
34  duplicate(other);
35 }
36 
38 KvpValueImpl::operator=(KvpValueImpl const & other) noexcept
39 {
40  duplicate(other);
41  return *this;
42 }
43 
45 {
46  datastore = b.datastore;
47  b.datastore = INT64_C(0);
48 }
49 
51 KvpValueImpl::operator=(KvpValueImpl && b) noexcept
52 {
53  std::swap (datastore, b.datastore);
54  return *this;
55 }
56 
59 {
60  /* If already a glist here, just append */
61  if (this->datastore.type() == typeid(GList*))
62  {
63  GList * list = boost::get<GList*>(datastore);
64  datastore = g_list_append (list, val);
65  return this;
66  }
67  /* If some other value, convert it to a glist */
68  GList *list = nullptr;
69 
70  list = g_list_append (list, this);
71  list = g_list_append (list, val);
72  return new KvpValueImpl(list);
73 }
74 
75 KvpValue::Type
76 KvpValueImpl::get_type() const noexcept
77 {
78  if (datastore.type() == typeid(int64_t))
79  return KvpValue::Type::INT64;
80  else if (datastore.type() == typeid(double))
81  return KvpValue::Type::DOUBLE;
82  else if (datastore.type() == typeid(gnc_numeric))
83  return KvpValue::Type::NUMERIC;
84  else if (datastore.type() == typeid(const gchar *))
85  return KvpValue::Type::STRING;
86  else if (datastore.type() == typeid(GncGUID *))
87  return KvpValue::Type::GUID;
88  else if (datastore.type() == typeid(Time64))
89  return KvpValue::Type::TIME64;
90  else if (datastore.type() == typeid(GList *))
91  return KvpValue::Type::GLIST;
92  else if (datastore.type() == typeid(KvpFrameImpl *))
93  return KvpValue::Type::FRAME;
94  else if (datastore.type() == typeid(GDate))
95  return KvpValue::Type::GDATE;
96 
97  return KvpValue::Type::INVALID;
98 }
99 
100 KvpFrame *
101 KvpValueImpl::replace_frame_nc (KvpFrame * new_value) noexcept
102 {
103  if (datastore.type() != typeid(KvpFrame *))
104  return {};
105  auto ret = boost::get<KvpFrame *>(datastore);
106  datastore = new_value;
107  return ret;
108 }
109 
110 GList *
111 KvpValueImpl::replace_glist_nc (GList * new_value) noexcept
112 {
113  if (datastore.type() != typeid(GList *))
114  return {};
115  auto ret = boost::get<GList *>(datastore);
116  datastore = new_value;
117  return ret;
118 }
119 
120 struct to_string_visitor : boost::static_visitor<void>
121 {
122  std::ostringstream & output;
123 
124  to_string_visitor(std::ostringstream & val) : output(val){}
125 
126  void operator()(int64_t val)
127  {
128  output << val << " (64-bit int)";
129  }
130 
131  void operator()(KvpFrame* val)
132  {
133  output << val->to_string();
134  }
135 
136  void operator()(GDate val)
137  {
138  output << std::setw(4) << g_date_get_year(&val) << '-';
139  output << std::setw(2) << g_date_get_month(&val) << '-';
140  output << std::setw(2) << g_date_get_day(&val);
141  output << " (gdate)";
142  }
143 
144  void operator()(GList * val)
145  {
146  output << "KVP_VALUE_GLIST(";
147  output << "[ ";
148  /*Since val is passed by value, we can modify it*/
149  for (;val; val = val->next)
150  {
151  auto realvalue = static_cast<const KvpValue *>(val->data);
152  output << ' ' << realvalue->to_string() << ',';
153  }
154  output << " ]";
155  output << ")";
156  }
157 
158  void operator()(Time64 val)
159  {
160  char tmp1[MAX_DATE_LENGTH + 1] {};
161  gnc_time64_to_iso8601_buff (val.t, tmp1);
162  output << tmp1 << " (time64)";
163  }
164 
165  void operator()(gnc_numeric val)
166  {
167  auto tmp1 = gnc_numeric_to_string(val);
168  if (tmp1)
169  {
170  output << tmp1;
171  g_free(tmp1);
172  }
173  else
174  {
175  output << "(null)";
176  }
177  output << " (gnc_numeric)";
178  }
179 
180  void operator()(GncGUID * val)
181  {
182  char guidstr[GUID_ENCODING_LENGTH+1];
183  if (val)
184  {
185  guid_to_string_buff(val,guidstr);
186  output << guidstr;
187  }
188  else
189  {
190  output << "(null)";
191  }
192  output << " (guid)";
193  }
194 
195  void operator()(const char * val)
196  {
197  output << val << " (char *)";
198  }
199 
200  void operator()(double val)
201  {
202  output << val << " (double)";
203  }
204 };
205 
206 std::string
207 KvpValueImpl::to_string(std::string const & prefix) const noexcept
208 {
209  if (this->datastore.type() == typeid(KvpFrame*))
210  return this->get<KvpFrame*>()->to_string(prefix);
211  std::ostringstream ret;
212  to_string_visitor visitor {ret};
213  boost::apply_visitor(visitor, datastore);
214  /*We still use g_strdup since the return value will be freed by g_free*/
215  return prefix + ret.str();
216 }
217 
218 std::string
219 KvpValueImpl::to_string() const noexcept
220 {
221  return to_string("");
222 }
223 
224 static int
225 kvp_glist_compare(const GList * list1, const GList * list2)
226 {
227  const GList *lp1;
228  const GList *lp2;
229 
230  if (list1 == list2) return 0;
231 
232  /* Nothing is always less than something */
233  if (!list1 && list2) return -1;
234  if (list1 && !list2) return 1;
235 
236  lp1 = list1;
237  lp2 = list2;
238  while (lp1 && lp2)
239  {
240  KvpValue *v1 = (KvpValue *) lp1->data;
241  KvpValue *v2 = (KvpValue *) lp2->data;
242  gint vcmp = compare(v1, v2);
243  if (vcmp != 0) return vcmp;
244  lp1 = lp1->next;
245  lp2 = lp2->next;
246  }
247  if (!lp1 && lp2) return -1;
248  if (!lp2 && lp1) return 1;
249  return 0;
250 }
251 
252 static GList *
253 kvp_glist_copy(const GList * list)
254 {
255  GList * retval = NULL;
256  GList * lptr;
257 
258  if (!list) return retval;
259 
260  /* Duplicate the backbone of the list (this duplicates the POINTERS
261  * to the values; we need to deep-copy the values separately) */
262  retval = g_list_copy((GList *) list);
263 
264  /* This step deep-copies the values */
265  for (lptr = retval; lptr; lptr = lptr->next)
266  {
267  lptr->data = new KvpValue(*static_cast<KvpValue *>(lptr->data));
268  }
269 
270  return retval;
271 }
272 
273 struct compare_visitor : boost::static_visitor<int>
274 {
275  template <typename T, typename U>
276  int operator()( T& one, U& two) const
277  {
278  throw std::invalid_argument{"You may not compare objects of different type."};
279  }
280 
281  template <typename T>
282  int operator()( T & one, T & two) const
283  {
284  /*This will work for any type that implements < and ==.*/
285  if (one < two)
286  return -1;
287  if (two < one)
288  return 1;
289  return 0;
290  }
291 };
292 template <> int compare_visitor::operator()(const char * const & one, const char * const & two) const
293 {
294  return strcmp(one, two);
295 }
296 template <> int compare_visitor::operator()(gnc_numeric const & one, gnc_numeric const & two) const
297 {
298  return gnc_numeric_compare(one, two);
299 }
300 template <> int compare_visitor::operator()(GncGUID * const & one, GncGUID * const & two) const
301 {
302  return guid_compare(one, two);
303 }
304 template <> int compare_visitor::operator()(Time64 const & one, Time64 const & two) const
305 {
306  return one.t < two.t ? -1 : one.t > two.t ? 1 : 0;
307 }
308 template <> int compare_visitor::operator()(GDate const & one, GDate const & two) const
309 {
310  return g_date_compare(&one,&two);
311 }
312 template <> int compare_visitor::operator()(GList * const & one, GList * const & two) const
313 {
314  return kvp_glist_compare(one, two);
315 }
316 template <> int compare_visitor::operator()(KvpFrame * const & one, KvpFrame * const & two) const
317 {
318  return compare(one, two);
319 }
320 template <> int compare_visitor::operator()(double const & one, double const & two) const
321 {
322  if (std::isnan(one) && std::isnan(two))
323  return 0;
324  if (one < two) return -1;
325  if (two < one) return 1;
326  return 0;
327 }
328 
329 int compare(const KvpValueImpl & one, const KvpValueImpl & two) noexcept
330 {
331  auto type1 = one.get_type();
332  auto type2 = two.get_type();
333 
334  if (type1 != type2)
335  return type1 < type2 ? -1 : 1;
336 
337  compare_visitor comparer;
338  return boost::apply_visitor(comparer, one.datastore, two.datastore);
339 }
340 
341 int
342 compare(const KvpValueImpl * one, const KvpValueImpl * two) noexcept
343 {
344  if (one == two) return 0;
345  if (one && !two) return 1;
346  if (!one && two) return -1;
347  assert (one && two); /* Silence a static analysis warning. */
348  return compare(*one, *two);
349 }
350 
351 struct delete_visitor : boost::static_visitor<void>
352 {
353  template <typename T> void
354  operator()(T &) { /*do nothing*/ }
355 };
356 
357 static void
358 destroy_value(void* item)
359 {
360  delete static_cast<KvpValue*>(item);
361 }
362 
363 template <> void
364 delete_visitor::operator()(GList * & value)
365 {
366  g_list_free_full(value, destroy_value);
367 }
368 template <> void
369 delete_visitor::operator()(const gchar * & value)
370 {
371  g_free(const_cast<gchar *> (value));
372 }
373 template <> void
374 delete_visitor::operator()(GncGUID * & value)
375 {
376  guid_free(value);
377 }
378 template <> void
379 delete_visitor::operator()(KvpFrame * & value)
380 {
381  delete value;
382 }
383 
385 {
386  delete_visitor d;
387  boost::apply_visitor(d, datastore);
388 }
389 
390 void
391 KvpValueImpl::duplicate(const KvpValueImpl& other) noexcept
392 {
393  if (other.datastore.type() == typeid(const gchar *))
394  this->datastore = const_cast<const gchar *>(g_strdup(other.get<const gchar *>()));
395  else if (other.datastore.type() == typeid(GncGUID*))
396  this->datastore = guid_copy(other.get<GncGUID *>());
397  else if (other.datastore.type() == typeid(GList*))
398  this->datastore = kvp_glist_copy(other.get<GList *>());
399  else if (other.datastore.type() == typeid(KvpFrame*))
400  this->datastore = new KvpFrame(*other.get<KvpFrame *>());
401  else
402  this->datastore = other.datastore;
403 }
404 
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
KvpFrame * replace_frame_nc(KvpFrame *) noexcept
Replaces the frame within this KvpValueImpl.
Definition: kvp-value.cpp:101
gchar * guid_to_string_buff(const GncGUID *guid, gchar *str)
The guid_to_string_buff() routine puts a null-terminated string encoding of the id into the memory po...
Definition: guid.cpp:174
int gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
Returns 1 if a>b, -1 if b>a, 0 if a == b.
gchar * gnc_numeric_to_string(gnc_numeric n)
Convert to string.
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition: guid.h:84
KvpValueImpl * add(KvpValueImpl *) noexcept
Adds another value to this KvpValueImpl.
Definition: kvp-value.cpp:58
Implements KvpValue using boost::variant.
Definition: kvp-value.hpp:57
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
Definition: gnc-date.h:114
KvpValueImpl(KvpValueImpl const &) noexcept
Performs a deep copy.
Definition: kvp-value.cpp:32
~KvpValueImpl() noexcept
Performs a deep delete.
Definition: kvp-value.cpp:384
GList * replace_glist_nc(GList *) noexcept
Replaces the glist within this KvpValueImpl.
Definition: kvp-value.cpp:111
The type used to store guids in C.
Definition: guid.h:75
char * gnc_time64_to_iso8601_buff(time64 time, char *buff)
The gnc_time64_to_iso8601_buff() routine takes the input UTC time64 value and prints it as an ISO-860...
Definition: gnc-date.cpp:1142