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 14866 : KvpValueImpl::KvpValueImpl(KvpValueImpl const & other) noexcept
35 : {
36 14866 : duplicate(other);
37 14866 : }
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 18487 : KvpValueImpl::get_type() const noexcept
79 : {
80 18487 : if (datastore.type() == type_id<int64_t>())
81 5800 : return KvpValue::Type::INT64;
82 12687 : else if (datastore.type() == type_id<double>())
83 89 : return KvpValue::Type::DOUBLE;
84 12598 : else if (datastore.type() == type_id<gnc_numeric>())
85 2547 : return KvpValue::Type::NUMERIC;
86 10051 : else if (datastore.type() == type_id<const gchar *>())
87 3280 : return KvpValue::Type::STRING;
88 6771 : else if (datastore.type() == type_id<GncGUID *>())
89 3248 : return KvpValue::Type::GUID;
90 3523 : else if (datastore.type() == type_id<Time64>())
91 46 : return KvpValue::Type::TIME64;
92 3477 : else if (datastore.type() == type_id<GList *>())
93 1526 : return KvpValue::Type::GLIST;
94 1951 : else if (datastore.type() == type_id<KvpFrameImpl *>())
95 1947 : 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 1765 : int operator()( T & one, T & two) const
265 : {
266 : /*This will work for any type that implements < and ==.*/
267 1765 : if (one < two)
268 0 : return -1;
269 1765 : if (two < one)
270 0 : return 1;
271 1765 : 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 2 : template <> int compare_visitor::operator()(Time64 const & one, Time64 const & two) const
287 : {
288 2 : 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 16899 : 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 6023 : delete_visitor::operator()(const gchar * & value)
352 : {
353 6023 : g_free(const_cast<gchar *> (value));
354 6023 : }
355 : template <> void
356 5295 : delete_visitor::operator()(GncGUID * & value)
357 : {
358 5295 : guid_free(value);
359 5295 : }
360 : template <> void
361 3066 : delete_visitor::operator()(KvpFrame * & value)
362 : {
363 3066 : delete value;
364 3066 : }
365 :
366 34329 : KvpValueImpl::~KvpValueImpl() noexcept
367 : {
368 : delete_visitor d;
369 34329 : boost::apply_visitor(d, datastore);
370 34329 : }
371 :
372 : void
373 14866 : KvpValueImpl::duplicate(const KvpValueImpl& other) noexcept
374 : {
375 14866 : if (other.datastore.type() == type_id<const gchar *>())
376 5056 : this->datastore = const_cast<const gchar *>(g_strdup(other.get<const gchar *>()));
377 12338 : else if (other.datastore.type() == type_id<GncGUID*>())
378 2412 : this->datastore = guid_copy(other.get<GncGUID *>());
379 9926 : else if (other.datastore.type() == type_id<GList*>())
380 1362 : this->datastore = kvp_glist_copy(other.get<GList *>());
381 8564 : else if (other.datastore.type() == type_id<KvpFrame*>())
382 1451 : this->datastore = new KvpFrame(*other.get<KvpFrame *>());
383 : else
384 7113 : this->datastore = other.datastore;
385 14866 : }
386 :
|