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