GnuCash  5.6-150-g038405b370+
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 struct to_string_visitor : boost::static_visitor<void>
103 {
104  std::ostringstream & output;
105 
106  to_string_visitor(std::ostringstream & val) : output(val){}
107 
108  void operator()(int64_t val)
109  {
110  output << val << " (64-bit int)";
111  }
112 
113  void operator()(KvpFrame* val)
114  {
115  output << val->to_string();
116  }
117 
118  void operator()(GDate val)
119  {
120  output << std::setw(4) << g_date_get_year(&val) << '-';
121  output << std::setw(2) << g_date_get_month(&val) << '-';
122  output << std::setw(2) << g_date_get_day(&val);
123  output << " (gdate)";
124  }
125 
126  void operator()(GList * val)
127  {
128  output << "KVP_VALUE_GLIST(";
129  output << "[ ";
130  /*Since val is passed by value, we can modify it*/
131  for (;val; val = val->next)
132  {
133  auto realvalue = static_cast<const KvpValue *>(val->data);
134  output << ' ' << realvalue->to_string() << ',';
135  }
136  output << " ]";
137  output << ")";
138  }
139 
140  void operator()(Time64 val)
141  {
142  char tmp1[MAX_DATE_LENGTH + 1] {};
143  gnc_time64_to_iso8601_buff (val.t, tmp1);
144  output << tmp1 << " (time64)";
145  }
146 
147  void operator()(gnc_numeric val)
148  {
149  auto tmp1 = gnc_numeric_to_string(val);
150  if (tmp1)
151  {
152  output << tmp1;
153  g_free(tmp1);
154  }
155  else
156  {
157  output << "(null)";
158  }
159  output << " (gnc_numeric)";
160  }
161 
162  void operator()(GncGUID * val)
163  {
164  char guidstr[GUID_ENCODING_LENGTH+1];
165  if (val)
166  {
167  guid_to_string_buff(val,guidstr);
168  output << guidstr;
169  }
170  else
171  {
172  output << "(null)";
173  }
174  output << " (guid)";
175  }
176 
177  void operator()(const char * val)
178  {
179  output << val << " (char *)";
180  }
181 
182  void operator()(double val)
183  {
184  output << val << " (double)";
185  }
186 };
187 
188 std::string
189 KvpValueImpl::to_string(std::string const & prefix) const noexcept
190 {
191  if (this->datastore.type() == type_id<KvpFrame*>())
192  return this->get<KvpFrame*>()->to_string(prefix);
193  std::ostringstream ret;
194  to_string_visitor visitor {ret};
195  boost::apply_visitor(visitor, datastore);
196  /*We still use g_strdup since the return value will be freed by g_free*/
197  return prefix + ret.str();
198 }
199 
200 std::string
201 KvpValueImpl::to_string() const noexcept
202 {
203  return to_string("");
204 }
205 
206 static int
207 kvp_glist_compare(const GList * list1, const GList * list2)
208 {
209  const GList *lp1;
210  const GList *lp2;
211 
212  if (list1 == list2) return 0;
213 
214  /* Nothing is always less than something */
215  if (!list1 && list2) return -1;
216  if (list1 && !list2) return 1;
217 
218  lp1 = list1;
219  lp2 = list2;
220  while (lp1 && lp2)
221  {
222  KvpValue *v1 = (KvpValue *) lp1->data;
223  KvpValue *v2 = (KvpValue *) lp2->data;
224  gint vcmp = compare(v1, v2);
225  if (vcmp != 0) return vcmp;
226  lp1 = lp1->next;
227  lp2 = lp2->next;
228  }
229  if (!lp1 && lp2) return -1;
230  if (!lp2 && lp1) return 1;
231  return 0;
232 }
233 
234 static GList *
235 kvp_glist_copy(const GList * list)
236 {
237  GList * retval = NULL;
238  GList * lptr;
239 
240  if (!list) return retval;
241 
242  /* Duplicate the backbone of the list (this duplicates the POINTERS
243  * to the values; we need to deep-copy the values separately) */
244  retval = g_list_copy((GList *) list);
245 
246  /* This step deep-copies the values */
247  for (lptr = retval; lptr; lptr = lptr->next)
248  {
249  lptr->data = new KvpValue(*static_cast<KvpValue *>(lptr->data));
250  }
251 
252  return retval;
253 }
254 
255 struct compare_visitor : boost::static_visitor<int>
256 {
257  template <typename T, typename U>
258  int operator()( T& one, U& two) const
259  {
260  throw std::invalid_argument{"You may not compare objects of different type."};
261  }
262 
263  template <typename T>
264  int operator()( T & one, T & two) const
265  {
266  /*This will work for any type that implements < and ==.*/
267  if (one < two)
268  return -1;
269  if (two < one)
270  return 1;
271  return 0;
272  }
273 };
274 template <> int compare_visitor::operator()(const char * const & one, const char * const & two) const
275 {
276  return strcmp(one, two);
277 }
278 template <> int compare_visitor::operator()(gnc_numeric const & one, gnc_numeric const & two) const
279 {
280  return gnc_numeric_compare(one, two);
281 }
282 template <> int compare_visitor::operator()(GncGUID * const & one, GncGUID * const & two) const
283 {
284  return guid_compare(one, two);
285 }
286 template <> int compare_visitor::operator()(Time64 const & one, Time64 const & two) const
287 {
288  return one.t < two.t ? -1 : one.t > two.t ? 1 : 0;
289 }
290 template <> int compare_visitor::operator()(GDate const & one, GDate const & two) const
291 {
292  return g_date_compare(&one,&two);
293 }
294 template <> int compare_visitor::operator()(GList * const & one, GList * const & two) const
295 {
296  return kvp_glist_compare(one, two);
297 }
298 template <> int compare_visitor::operator()(KvpFrame * const & one, KvpFrame * const & two) const
299 {
300  return compare(one, two);
301 }
302 template <> int compare_visitor::operator()(double const & one, double const & two) const
303 {
304  if (std::isnan(one) && std::isnan(two))
305  return 0;
306  if (one < two) return -1;
307  if (two < one) return 1;
308  return 0;
309 }
310 
311 int compare(const KvpValueImpl & one, const KvpValueImpl & two) noexcept
312 {
313  auto type1 = one.get_type();
314  auto type2 = two.get_type();
315 
316  if (type1 != type2)
317  return type1 < type2 ? -1 : 1;
318 
319  compare_visitor comparer;
320  return boost::apply_visitor(comparer, one.datastore, two.datastore);
321 }
322 
323 int
324 compare(const KvpValueImpl * one, const KvpValueImpl * two) noexcept
325 {
326  if (one == two) return 0;
327  if (one && !two) return 1;
328  if (!one && two) return -1;
329  assert (one && two); /* Silence a static analysis warning. */
330  return compare(*one, *two);
331 }
332 
333 struct delete_visitor : boost::static_visitor<void>
334 {
335  template <typename T> void
336  operator()(T &) { /*do nothing*/ }
337 };
338 
339 static void
340 destroy_value(void* item)
341 {
342  delete static_cast<KvpValue*>(item);
343 }
344 
345 template <> void
346 delete_visitor::operator()(GList * & value)
347 {
348  g_list_free_full(value, destroy_value);
349 }
350 template <> void
351 delete_visitor::operator()(const gchar * & value)
352 {
353  g_free(const_cast<gchar *> (value));
354 }
355 template <> void
356 delete_visitor::operator()(GncGUID * & value)
357 {
358  guid_free(value);
359 }
360 template <> void
361 delete_visitor::operator()(KvpFrame * & value)
362 {
363  delete value;
364 }
365 
367 {
368  delete_visitor d;
369  boost::apply_visitor(d, datastore);
370 }
371 
372 void
373 KvpValueImpl::duplicate(const KvpValueImpl& other) noexcept
374 {
375  if (other.datastore.type() == type_id<const gchar *>())
376  this->datastore = const_cast<const gchar *>(g_strdup(other.get<const gchar *>()));
377  else if (other.datastore.type() == type_id<GncGUID*>())
378  this->datastore = guid_copy(other.get<GncGUID *>());
379  else if (other.datastore.type() == type_id<GList*>())
380  this->datastore = kvp_glist_copy(other.get<GList *>());
381  else if (other.datastore.type() == type_id<KvpFrame*>())
382  this->datastore = new KvpFrame(*other.get<KvpFrame *>());
383  else
384  this->datastore = other.datastore;
385 }
386 
GncGUID * guid_copy(const GncGUID *guid)
Returns a newly allocated GncGUID that matches the passed-in GUID.
Definition: guid.cpp:120
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:173
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:51
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
Definition: gnc-date.h:108
KvpValueImpl(KvpValueImpl const &) noexcept
Performs a deep copy.
Definition: kvp-value.cpp:34
~KvpValueImpl() noexcept
Performs a deep delete.
Definition: kvp-value.cpp:366
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:1139