GnuCash  4.12-404-geb24099a91
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 
32 using boost::typeindex::type_id;
33 
35 {
36  duplicate(other);
37 }
38 
40 KvpValueImpl::operator=(KvpValueImpl const & other) noexcept
41 {
42  duplicate(other);
43  return *this;
44 }
45 
47 {
48  datastore = b.datastore;
49  b.datastore = INT64_C(0);
50 }
51 
53 KvpValueImpl::operator=(KvpValueImpl && b) noexcept
54 {
55  std::swap (datastore, b.datastore);
56  return *this;
57 }
58 
61 {
62  /* If already a glist here, just append */
63  if (this->datastore.type() == type_id<GList*>())
64  {
65  GList * list = boost::get<GList*>(datastore);
66  datastore = g_list_append (list, val);
67  return this;
68  }
69  /* If some other value, convert it to a glist */
70  GList *list = nullptr;
71 
72  list = g_list_append (list, this);
73  list = g_list_append (list, val);
74  return new KvpValueImpl(list);
75 }
76 
77 KvpValue::Type
78 KvpValueImpl::get_type() const noexcept
79 {
80  if (datastore.type() == type_id<int64_t>())
81  return KvpValue::Type::INT64;
82  else if (datastore.type() == type_id<double>())
83  return KvpValue::Type::DOUBLE;
84  else if (datastore.type() == type_id<gnc_numeric>())
85  return KvpValue::Type::NUMERIC;
86  else if (datastore.type() == type_id<const gchar *>())
87  return KvpValue::Type::STRING;
88  else if (datastore.type() == type_id<GncGUID *>())
89  return KvpValue::Type::GUID;
90  else if (datastore.type() == type_id<Time64>())
91  return KvpValue::Type::TIME64;
92  else if (datastore.type() == type_id<GList *>())
93  return KvpValue::Type::GLIST;
94  else if (datastore.type() == type_id<KvpFrameImpl *>())
95  return KvpValue::Type::FRAME;
96  else if (datastore.type() == type_id<GDate>())
97  return KvpValue::Type::GDATE;
98 
99  return KvpValue::Type::INVALID;
100 }
101 
102 KvpFrame *
103 KvpValueImpl::replace_frame_nc (KvpFrame * new_value) noexcept
104 {
105  if (datastore.type() != type_id<KvpFrame *>())
106  return {};
107  auto ret = boost::get<KvpFrame *>(datastore);
108  datastore = new_value;
109  return ret;
110 }
111 
112 GList *
113 KvpValueImpl::replace_glist_nc (GList * new_value) noexcept
114 {
115  if (datastore.type() != type_id<GList *>())
116  return {};
117  auto ret = boost::get<GList *>(datastore);
118  datastore = new_value;
119  return ret;
120 }
121 
122 struct to_string_visitor : boost::static_visitor<void>
123 {
124  std::ostringstream & output;
125 
126  to_string_visitor(std::ostringstream & val) : output(val){}
127 
128  void operator()(int64_t val)
129  {
130  output << val << " (64-bit int)";
131  }
132 
133  void operator()(KvpFrame* val)
134  {
135  output << val->to_string();
136  }
137 
138  void operator()(GDate val)
139  {
140  output << std::setw(4) << g_date_get_year(&val) << '-';
141  output << std::setw(2) << g_date_get_month(&val) << '-';
142  output << std::setw(2) << g_date_get_day(&val);
143  output << " (gdate)";
144  }
145 
146  void operator()(GList * val)
147  {
148  output << "KVP_VALUE_GLIST(";
149  output << "[ ";
150  /*Since val is passed by value, we can modify it*/
151  for (;val; val = val->next)
152  {
153  auto realvalue = static_cast<const KvpValue *>(val->data);
154  output << ' ' << realvalue->to_string() << ',';
155  }
156  output << " ]";
157  output << ")";
158  }
159 
160  void operator()(Time64 val)
161  {
162  char tmp1[MAX_DATE_LENGTH + 1] {};
163  gnc_time64_to_iso8601_buff (val.t, tmp1);
164  output << tmp1 << " (time64)";
165  }
166 
167  void operator()(gnc_numeric val)
168  {
169  auto tmp1 = gnc_numeric_to_string(val);
170  if (tmp1)
171  {
172  output << tmp1;
173  g_free(tmp1);
174  }
175  else
176  {
177  output << "(null)";
178  }
179  output << " (gnc_numeric)";
180  }
181 
182  void operator()(GncGUID * val)
183  {
184  char guidstr[GUID_ENCODING_LENGTH+1];
185  if (val)
186  {
187  guid_to_string_buff(val,guidstr);
188  output << guidstr;
189  }
190  else
191  {
192  output << "(null)";
193  }
194  output << " (guid)";
195  }
196 
197  void operator()(const char * val)
198  {
199  output << val << " (char *)";
200  }
201 
202  void operator()(double val)
203  {
204  output << val << " (double)";
205  }
206 };
207 
208 std::string
209 KvpValueImpl::to_string(std::string const & prefix) const noexcept
210 {
211  if (this->datastore.type() == type_id<KvpFrame*>())
212  return this->get<KvpFrame*>()->to_string(prefix);
213  std::ostringstream ret;
214  to_string_visitor visitor {ret};
215  boost::apply_visitor(visitor, datastore);
216  /*We still use g_strdup since the return value will be freed by g_free*/
217  return prefix + ret.str();
218 }
219 
220 std::string
221 KvpValueImpl::to_string() const noexcept
222 {
223  return to_string("");
224 }
225 
226 static int
227 kvp_glist_compare(const GList * list1, const GList * list2)
228 {
229  const GList *lp1;
230  const GList *lp2;
231 
232  if (list1 == list2) return 0;
233 
234  /* Nothing is always less than something */
235  if (!list1 && list2) return -1;
236  if (list1 && !list2) return 1;
237 
238  lp1 = list1;
239  lp2 = list2;
240  while (lp1 && lp2)
241  {
242  KvpValue *v1 = (KvpValue *) lp1->data;
243  KvpValue *v2 = (KvpValue *) lp2->data;
244  gint vcmp = compare(v1, v2);
245  if (vcmp != 0) return vcmp;
246  lp1 = lp1->next;
247  lp2 = lp2->next;
248  }
249  if (!lp1 && lp2) return -1;
250  if (!lp2 && lp1) return 1;
251  return 0;
252 }
253 
254 static GList *
255 kvp_glist_copy(const GList * list)
256 {
257  GList * retval = NULL;
258  GList * lptr;
259 
260  if (!list) return retval;
261 
262  /* Duplicate the backbone of the list (this duplicates the POINTERS
263  * to the values; we need to deep-copy the values separately) */
264  retval = g_list_copy((GList *) list);
265 
266  /* This step deep-copies the values */
267  for (lptr = retval; lptr; lptr = lptr->next)
268  {
269  lptr->data = new KvpValue(*static_cast<KvpValue *>(lptr->data));
270  }
271 
272  return retval;
273 }
274 
275 struct compare_visitor : boost::static_visitor<int>
276 {
277  template <typename T, typename U>
278  int operator()( T& one, U& two) const
279  {
280  throw std::invalid_argument{"You may not compare objects of different type."};
281  }
282 
283  template <typename T>
284  int operator()( T & one, T & two) const
285  {
286  /*This will work for any type that implements < and ==.*/
287  if (one < two)
288  return -1;
289  if (two < one)
290  return 1;
291  return 0;
292  }
293 };
294 template <> int compare_visitor::operator()(const char * const & one, const char * const & two) const
295 {
296  return strcmp(one, two);
297 }
298 template <> int compare_visitor::operator()(gnc_numeric const & one, gnc_numeric const & two) const
299 {
300  return gnc_numeric_compare(one, two);
301 }
302 template <> int compare_visitor::operator()(GncGUID * const & one, GncGUID * const & two) const
303 {
304  return guid_compare(one, two);
305 }
306 template <> int compare_visitor::operator()(Time64 const & one, Time64 const & two) const
307 {
308  return one.t < two.t ? -1 : one.t > two.t ? 1 : 0;
309 }
310 template <> int compare_visitor::operator()(GDate const & one, GDate const & two) const
311 {
312  return g_date_compare(&one,&two);
313 }
314 template <> int compare_visitor::operator()(GList * const & one, GList * const & two) const
315 {
316  return kvp_glist_compare(one, two);
317 }
318 template <> int compare_visitor::operator()(KvpFrame * const & one, KvpFrame * const & two) const
319 {
320  return compare(one, two);
321 }
322 template <> int compare_visitor::operator()(double const & one, double const & two) const
323 {
324  if (std::isnan(one) && std::isnan(two))
325  return 0;
326  if (one < two) return -1;
327  if (two < one) return 1;
328  return 0;
329 }
330 
331 int compare(const KvpValueImpl & one, const KvpValueImpl & two) noexcept
332 {
333  auto type1 = one.get_type();
334  auto type2 = two.get_type();
335 
336  if (type1 != type2)
337  return type1 < type2 ? -1 : 1;
338 
339  compare_visitor comparer;
340  return boost::apply_visitor(comparer, one.datastore, two.datastore);
341 }
342 
343 int
344 compare(const KvpValueImpl * one, const KvpValueImpl * two) noexcept
345 {
346  if (one == two) return 0;
347  if (one && !two) return 1;
348  if (!one && two) return -1;
349  assert (one && two); /* Silence a static analysis warning. */
350  return compare(*one, *two);
351 }
352 
353 struct delete_visitor : boost::static_visitor<void>
354 {
355  template <typename T> void
356  operator()(T &) { /*do nothing*/ }
357 };
358 
359 static void
360 destroy_value(void* item)
361 {
362  delete static_cast<KvpValue*>(item);
363 }
364 
365 template <> void
366 delete_visitor::operator()(GList * & value)
367 {
368  g_list_free_full(value, destroy_value);
369 }
370 template <> void
371 delete_visitor::operator()(const gchar * & value)
372 {
373  g_free(const_cast<gchar *> (value));
374 }
375 template <> void
376 delete_visitor::operator()(GncGUID * & value)
377 {
378  guid_free(value);
379 }
380 template <> void
381 delete_visitor::operator()(KvpFrame * & value)
382 {
383  delete value;
384 }
385 
387 {
388  delete_visitor d;
389  boost::apply_visitor(d, datastore);
390 }
391 
392 void
393 KvpValueImpl::duplicate(const KvpValueImpl& other) noexcept
394 {
395  if (other.datastore.type() == type_id<const gchar *>())
396  this->datastore = const_cast<const gchar *>(g_strdup(other.get<const gchar *>()));
397  else if (other.datastore.type() == type_id<GncGUID*>())
398  this->datastore = guid_copy(other.get<GncGUID *>());
399  else if (other.datastore.type() == type_id<GList*>())
400  this->datastore = kvp_glist_copy(other.get<GList *>());
401  else if (other.datastore.type() == type_id<KvpFrame*>())
402  this->datastore = new KvpFrame(*other.get<KvpFrame *>());
403  else
404  this->datastore = other.datastore;
405 }
406 
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:103
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:60
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:34
~KvpValueImpl() noexcept
Performs a deep delete.
Definition: kvp-value.cpp:386
GList * replace_glist_nc(GList *) noexcept
Replaces the glist within this KvpValueImpl.
Definition: kvp-value.cpp:113
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