GnuCash c935c2f+
Loading...
Searching...
No Matches
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
32using boost::typeindex::type_id;
33
35{
36 duplicate(other);
37}
38
40KvpValueImpl::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
53KvpValueImpl::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
77KvpValue::Type
78KvpValueImpl::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
102struct 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
188std::string
189KvpValueImpl::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
200std::string
201KvpValueImpl::to_string() const noexcept
202{
203 return to_string("");
204}
205
206static int
207kvp_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
234static GList *
235kvp_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
255struct 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};
274template <> int compare_visitor::operator()(const char * const & one, const char * const & two) const
275{
276 return strcmp(one, two);
277}
278template <> int compare_visitor::operator()(gnc_numeric const & one, gnc_numeric const & two) const
279{
280 return gnc_numeric_compare(one, two);
281}
282template <> int compare_visitor::operator()(GncGUID * const & one, GncGUID * const & two) const
283{
284 return guid_compare(one, two);
285}
286template <> 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}
290template <> int compare_visitor::operator()(GDate const & one, GDate const & two) const
291{
292 return g_date_compare(&one,&two);
293}
294template <> int compare_visitor::operator()(GList * const & one, GList * const & two) const
295{
296 return kvp_glist_compare(one, two);
297}
298template <> int compare_visitor::operator()(KvpFrame * const & one, KvpFrame * const & two) const
299{
300 return compare(one, two);
301}
302template <> 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
311int 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
323int
324compare(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
333struct delete_visitor : boost::static_visitor<void>
334{
335 template <typename T> void
336 operator()(T &) { /*do nothing*/ }
337};
338
339static void
340destroy_value(void* item)
341{
342 delete static_cast<KvpValue*>(item);
343}
344
345template <> void
346delete_visitor::operator()(GList * & value)
347{
348 g_list_free_full(value, destroy_value);
349}
350template <> void
351delete_visitor::operator()(const gchar * & value)
352{
353 g_free(const_cast<gchar *> (value));
354}
355template <> void
356delete_visitor::operator()(GncGUID * & value)
357{
358 guid_free(value);
359}
360template <> void
361delete_visitor::operator()(KvpFrame * & value)
362{
363 delete value;
364}
365
367{
369 boost::apply_visitor(d, datastore);
370}
371
372void
373KvpValueImpl::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
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...
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
Definition gnc-date.h:108
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition guid.h:84
GncGUID * guid_copy(const GncGUID *guid)
Returns a newly allocated GncGUID that matches the passed-in GUID.
Definition guid.cpp:155
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:208
gchar * gnc_numeric_to_string(gnc_numeric n)
Convert to string.
int gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
Returns 1 if a>b, -1 if b>a, 0 if a == b
The type used to store guids in C.
Definition guid.h:75
Implements KvpValue using boost::variant.
Definition kvp-value.hpp:53
KvpValueImpl * add(KvpValueImpl *) noexcept
Adds another value to this KvpValueImpl.
Definition kvp-value.cpp:60
~KvpValueImpl() noexcept
Performs a deep delete.
KvpValueImpl(KvpValueImpl const &) noexcept
Performs a deep copy.
Definition kvp-value.cpp:34