GUI: Move .ui files from goffice resources to glib resources
[gnumeric.git] / src / go-data-cache.c
blob64251031873537a727193d6f06453d201e505345
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * go-data-cache.h : The definition of a content for a data slicer
5 * Copyright (C) 2008 Jody Goldberg (jody@gnome.org)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) version 3.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
20 * USA
23 #include <gnumeric-config.h>
24 #include "go-data-cache-impl.h"
25 #include "go-data-cache-source.h"
26 #include "go-data-cache-field-impl.h"
28 #include <gsf/gsf-impl-utils.h>
29 #include <glib/gi18n-lib.h>
30 #include <string.h>
32 #define GO_DATA_CACHE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GO_DATA_CACHE_TYPE, GODataCacheClass))
33 #define IS_GO_DATA_CACHE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GO_DATA_CACHE_TYPE))
34 #define GO_DATA_CACHE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GO_DATA_CACHE_TYPE, GODataCacheClass))
36 enum {
37 PROP_0,
38 PROP_REFRESHED_BY, /* char * */
39 PROP_REFRESHED_ON, /* GOVal * */
40 PROP_REFRESH_UPGRADES, /* bool */
41 PROP_XL_REFRESH_VER, /* unsigned int */
42 PROP_XL_CREATED_VER /* unsigned int */
45 /*****************************************************************/
47 static void
48 go_data_cache_records_set_size (GODataCache *cache, unsigned int n)
50 int expand;
52 g_return_if_fail (cache->record_size > 0);
53 g_return_if_fail (n < G_MAXUINT / cache->record_size);
55 expand = n - cache->records_allocated;
56 if (0 == expand)
57 return;
59 cache->records = g_realloc (cache->records, n * cache->record_size);
60 if (expand > 0)
61 memset (cache->records + cache->records_allocated * cache->record_size, 0,
62 expand * cache->record_size);
63 cache->records_allocated = n;
66 static guint8 *
67 go_data_cache_records_fetch_index (GODataCache *cache, unsigned i)
69 if (cache->records_allocated <= i) {
70 go_data_cache_records_set_size (cache, i+128);
71 if (cache->records_allocated <= i)
72 return NULL;
75 if (cache->records_len <= i)
76 cache->records_len = i + 1;
78 return go_data_cache_records_index (cache, i);
81 static void
82 go_data_cache_records_init (GODataCache *cache, unsigned int n, unsigned int record_size)
84 cache->record_size = record_size;
85 cache->records_len = 0;
86 go_data_cache_records_set_size (cache, n);
89 /*****************************************************************/
91 static GObjectClass *parent_klass;
92 static void
93 go_data_cache_init (GODataCache *cache)
95 cache->fields = g_ptr_array_new ();
96 cache->data_source = NULL;
97 cache->records = NULL;
98 cache->records_len = cache->records_allocated = 0;
100 cache->refreshed_by = NULL;
101 cache->refreshed_on = NULL;
102 cache->refresh_upgrades = TRUE;
104 cache->XL_created_ver = 1;
105 cache->XL_refresh_ver = 1;
108 static void
109 go_data_cache_finalize (GObject *obj)
111 GODataCache *cache = (GODataCache *)obj;
112 unsigned i;
114 if (NULL != cache->records) {
115 for (i = cache->fields->len ; i-- > 0 ; ) {
116 GODataCacheField const *f = g_ptr_array_index (cache->fields, i);
117 if (GO_DATA_CACHE_FIELD_TYPE_INLINE == f->ref_type) {
118 unsigned j;
119 for (j = cache->records_len ; j-- > 0 ; ) {
120 GOVal *v;
121 gpointer p = go_data_cache_records_index (cache, j) + f->offset;
122 memcpy (&v, p, sizeof (v));
123 go_val_free (v);
127 g_free (cache->records);
128 cache->records = NULL;
129 cache->records_len = cache->records_allocated = 0;
132 for (i = cache->fields->len ; i-- > 0 ; )
133 g_object_unref (g_ptr_array_index (cache->fields, i));
134 g_ptr_array_free (cache->fields, TRUE);
135 cache->fields = NULL;
137 if (NULL != cache->data_source) {
138 g_object_unref (cache->data_source);
139 cache->data_source = NULL;
142 g_free (cache->refreshed_by);
143 go_val_free (cache->refreshed_on);
145 (parent_klass->finalize) (obj);
148 static void
149 go_data_cache_set_property (GObject *obj, guint property_id,
150 GValue const *value, GParamSpec *pspec)
152 GODataCache *cache = (GODataCache *)obj;
154 switch (property_id) {
155 case PROP_REFRESHED_BY:
156 g_free (cache->refreshed_by);
157 cache->refreshed_by = g_value_dup_string (value);
158 break;
159 case PROP_REFRESHED_ON:
160 go_val_free (cache->refreshed_on);
161 cache->refreshed_on = g_value_dup_boxed (value);
162 break;
163 case PROP_REFRESH_UPGRADES : cache->refresh_upgrades = g_value_get_boolean (value); break;
164 case PROP_XL_REFRESH_VER : cache->XL_refresh_ver = g_value_get_uint (value); break;
165 case PROP_XL_CREATED_VER : cache->XL_created_ver = g_value_get_uint (value); break;
167 default:
168 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
172 static void
173 go_data_cache_get_property (GObject *obj, guint property_id,
174 GValue *value, GParamSpec *pspec)
176 GODataCache const *cache = (GODataCache const *)obj;
177 switch (property_id) {
178 case PROP_REFRESHED_BY : g_value_set_string (value, cache->refreshed_by); break;
179 case PROP_REFRESHED_ON : g_value_set_boxed (value, cache->refreshed_on); break;
180 case PROP_REFRESH_UPGRADES : g_value_set_boolean (value, cache->refresh_upgrades); break;
181 case PROP_XL_REFRESH_VER : g_value_set_uint (value, cache->XL_refresh_ver); break;
182 case PROP_XL_CREATED_VER : g_value_set_uint (value, cache->XL_created_ver); break;
183 default:
184 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
188 static void
189 go_data_cache_class_init (GODataCacheClass *klass)
191 GObjectClass *gobject_class = (GObjectClass *)klass;
192 gobject_class->set_property = go_data_cache_set_property;
193 gobject_class->get_property = go_data_cache_get_property;
194 gobject_class->finalize = go_data_cache_finalize;
196 g_object_class_install_property (gobject_class, PROP_REFRESHED_BY,
197 g_param_spec_string ("refreshed-by", NULL, NULL, NULL,
198 GSF_PARAM_STATIC | G_PARAM_READWRITE));
199 g_object_class_install_property (gobject_class, PROP_REFRESHED_ON,
200 g_param_spec_boxed ("refreshed-on", NULL, NULL,
201 GO_VAL_TYPE, GSF_PARAM_STATIC | G_PARAM_READWRITE));
202 g_object_class_install_property (gobject_class, PROP_REFRESH_UPGRADES,
203 g_param_spec_boolean ("refresh-upgrades", NULL, NULL,
204 TRUE, GSF_PARAM_STATIC | G_PARAM_READWRITE));
205 g_object_class_install_property (gobject_class, PROP_XL_REFRESH_VER,
206 g_param_spec_uint ("refresh-version", NULL, NULL,
207 0, G_MAXUINT, 1, GSF_PARAM_STATIC | G_PARAM_READWRITE));
208 g_object_class_install_property (gobject_class, PROP_XL_CREATED_VER,
209 g_param_spec_uint ("created-version", NULL, NULL,
210 0, G_MAXUINT, 1, GSF_PARAM_STATIC | G_PARAM_READWRITE));
212 parent_klass = g_type_class_peek_parent (klass);
215 GSF_CLASS (GODataCache, go_data_cache,
216 go_data_cache_class_init, go_data_cache_init,
217 G_TYPE_OBJECT)
219 GODataCacheSource *
220 go_data_cache_get_source (GODataCache const *cache)
222 g_return_val_if_fail (IS_GO_DATA_CACHE (cache), NULL);
223 return cache->data_source;
227 * go_data_cache_set_source:
228 * @cache: #GODataCache
229 * @src: #GODataCacheSource
231 * Absorbs the reference to @src.
233 void
234 go_data_cache_set_source (GODataCache *cache, GODataCacheSource *src)
236 g_return_if_fail (IS_GO_DATA_CACHE (cache));
237 g_return_if_fail (NULL == src || IS_GO_DATA_CACHE_SOURCE (src));
239 if (cache->data_source)
240 g_object_unref (cache->data_source);
241 cache->data_source = src;
244 void
245 go_data_cache_add_field (GODataCache *cache, GODataCacheField *field)
247 g_return_if_fail (IS_GO_DATA_CACHE (cache));
248 g_return_if_fail (IS_GO_DATA_CACHE_FIELD (field));
249 g_return_if_fail (field->indx < 0);
250 g_return_if_fail (field->cache == NULL);
251 g_return_if_fail (NULL == cache->records);
253 field->indx = cache->fields->len;
254 field->cache = cache;
255 g_ptr_array_add (cache->fields, field);
259 * go_data_cache_import_start:
260 * @cache:#GODataCache
261 * @n: num records
263 * Validate the field setup and initialize the storage.
265 void
266 go_data_cache_import_start (GODataCache *cache, unsigned int n)
268 GODataCacheField *f;
269 unsigned int i, offset = 0;
271 g_return_if_fail (IS_GO_DATA_CACHE (cache));
272 g_return_if_fail (NULL == cache->records);
274 for (i = 0 ; i < cache->fields->len ; i++) {
275 f = g_ptr_array_index (cache->fields, i);
276 f->offset = offset;
277 if (NULL == f->indexed || 0 == f->indexed->len) {
278 if (NULL != f->grouped &&
279 f->group_parent >= 0 && f->group_parent != f->indx)
280 f->ref_type = GO_DATA_CACHE_FIELD_TYPE_NONE;
281 else {
282 offset += sizeof (GOVal *);
283 f->ref_type = GO_DATA_CACHE_FIELD_TYPE_INLINE;
285 } else if (f->indexed->len < ((1<<8) - 1)) {
286 offset += sizeof (guint8);
287 f->ref_type = GO_DATA_CACHE_FIELD_TYPE_INDEXED_I8;
288 } else if (f->indexed->len < ((1<<16) - 1)) {
289 offset += sizeof (guint16);
290 f->ref_type = GO_DATA_CACHE_FIELD_TYPE_INDEXED_I16;
291 } else {
292 offset += sizeof (guint32);
293 f->ref_type = GO_DATA_CACHE_FIELD_TYPE_INDEXED_I32;
297 for (i = 0 ; i < cache->fields->len ; i++) {
298 f = g_ptr_array_index (cache->fields, i);
299 if (f->group_parent >= 0) {
300 GODataCacheField *base = g_ptr_array_index (cache->fields, f->group_parent);
301 g_return_if_fail (base->ref_type != GO_DATA_CACHE_FIELD_TYPE_NONE);
302 f->offset = base->offset;
305 go_data_cache_records_init (cache, n, offset);
308 void
309 go_data_cache_dump_value (GOVal const *v)
311 if (NULL == v) {
312 g_print ("<MISSING>");
313 } else {
314 GOFormat const *fmt = go_val_get_fmt (v);
316 if (NULL != fmt) {
317 char *str = format_value (fmt, v, -1, NULL);
318 g_print ("'%s'", str);
319 g_free (str);
320 } else
321 g_print ("'%s'", value_peek_string (v));
325 void
326 go_data_cache_set_val (GODataCache *cache,
327 int field, unsigned int record_num, GOVal *v)
329 GODataCacheField *f;
330 gpointer p;
332 g_return_if_fail (IS_GO_DATA_CACHE (cache));
333 g_return_if_fail (NULL != cache->records);
334 g_return_if_fail (0 <= field && (unsigned int )field < cache->fields->len);
336 f = g_ptr_array_index (cache->fields, field);
338 #ifdef GO_DEBUG_SLICERS
339 g_print ("\t[%d] ", field);
340 go_data_cache_dump_value (v);
341 #endif
343 p = go_data_cache_records_fetch_index (cache, record_num) + f->offset;
344 switch (f->ref_type) {
345 case GO_DATA_CACHE_FIELD_TYPE_NONE:
346 g_warning ("attempt to set a value for grouped/calculated field #%d : '%s'",
347 f->indx, f->name->str);
348 return;
350 case GO_DATA_CACHE_FIELD_TYPE_INLINE:
351 memcpy (p, &v, sizeof (v));
352 return;
354 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I8 : *((guint8 *)p) = 0; break;
355 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I16 : *((guint16 *)p) = 0; break;
356 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I32 : *((guint32 *)p) = 0; break;
358 default:
359 g_warning ("unknown field type %d", f->ref_type);
361 go_val_free (v);
362 g_warning ("Attempt to store a value in an indexed field");
365 void
366 go_data_cache_set_index (GODataCache *cache,
367 int field, unsigned int record_num, unsigned int idx)
369 GODataCacheField *f;
370 gpointer p;
372 g_return_if_fail (IS_GO_DATA_CACHE (cache));
373 g_return_if_fail (NULL != cache->records);
374 g_return_if_fail (0 <= field && (unsigned int )field < cache->fields->len);
376 f = g_ptr_array_index (cache->fields, field);
378 g_return_if_fail (NULL != f->indexed);
379 g_return_if_fail (idx < f->indexed->len);
381 #ifdef GO_DEBUG_SLICERS
382 g_print ("\t(%d) %d=", field, idx);
383 go_data_cache_dump_value (cache, g_ptr_array_index (f->indexed, idx));
384 #endif
386 p = go_data_cache_records_fetch_index (cache, record_num) + f->offset;
387 switch (f->ref_type) {
388 case GO_DATA_CACHE_FIELD_TYPE_NONE:
389 g_warning ("attempt to get value from grouped/calculated field #%d : '%s'",
390 f->indx, f->name->str);
391 return;
392 case GO_DATA_CACHE_FIELD_TYPE_INLINE: {
393 GOVal *v = go_val_new_empty ();
394 memcpy (p, &v, sizeof (v));
395 break;
397 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I8 : *((guint8 *)p) = idx+1; break;
398 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I16 : *((guint16 *)p) = idx+1; break;
399 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I32 : *((guint32 *)p) = idx+1; break;
401 default:
402 g_warning ("unknown field type %d", f->ref_type);
407 * go_data_cache_import_done:
408 * @cache: #GODataCache
409 * @actual_records: count
411 * Tidy up after an import, and tighten up the amount of memory used to store
412 * the records.
414 void
415 go_data_cache_import_done (GODataCache *cache, unsigned int actual_records)
417 g_return_if_fail (IS_GO_DATA_CACHE (cache));
419 if (actual_records < cache->records_allocated)
420 go_data_cache_records_set_size (cache, actual_records);
423 unsigned int
424 go_data_cache_num_items (GODataCache const *cache)
426 g_return_val_if_fail (IS_GO_DATA_CACHE (cache), 0);
427 return cache->records_allocated;
430 unsigned int
431 go_data_cache_num_fields (GODataCache const *cache)
433 g_return_val_if_fail (IS_GO_DATA_CACHE (cache), 0);
434 return cache->fields->len;
436 GODataCacheField *
437 go_data_cache_get_field (GODataCache const *cache, int i)
439 g_return_val_if_fail (IS_GO_DATA_CACHE (cache), NULL);
440 g_return_val_if_fail (0 <= i && (unsigned int)i < cache->fields->len, NULL);
441 return g_ptr_array_index (cache->fields, i);
445 go_data_cache_get_index (GODataCache const *cache,
446 GODataCacheField const *field, unsigned int record_num)
448 gpointer p;
450 g_return_val_if_fail (IS_GO_DATA_CACHE (cache), -1);
452 p = go_data_cache_records_index (cache, record_num) + field->offset;
453 switch (field->ref_type) {
454 case GO_DATA_CACHE_FIELD_TYPE_NONE : break;
455 case GO_DATA_CACHE_FIELD_TYPE_INLINE : break;
456 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I8 : return *(guint8 *)p - 1;
457 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I16 : return *(guint16 *)p - 1;
458 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I32 : return *(guint32 *)p - 1;
459 default:
460 g_warning ("unknown field type %d", field->ref_type);
462 return -1;
465 typedef struct {
466 GODataCache const *cache;
467 GArray const *field_order;
468 } GODataCacheCompare;
469 static gint
470 cb_go_data_cache_cmp (int const *a, int const * b,
471 GODataCacheCompare const *info)
473 GODataCacheField const *f, *base;
474 GOVal const *va, *vb;
475 gpointer pa, pb;
476 unsigned int idxa, idxb, i;
477 unsigned int const n = info->field_order->len;
478 int res;
480 for (i = 0 ; i < n ; i++) {
481 f = g_ptr_array_index (info->cache->fields, g_array_index (info->field_order, unsigned int, i));
482 base = (f->group_parent < 0) ? f : g_ptr_array_index (info->cache->fields, f->group_parent);
483 pa = go_data_cache_records_index (info->cache, *a) + base->offset;
484 pb = go_data_cache_records_index (info->cache, *b) + base->offset;
485 if (base->ref_type != GO_DATA_CACHE_FIELD_TYPE_INLINE) {
486 switch (base->ref_type) {
487 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I8 :
488 idxa = *(guint8 *)pa;
489 idxb = *(guint8 *)pb;
490 break;
491 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I16:
492 idxa = *(guint16 *)pa;
493 idxb = *(guint16 *)pb;
494 break;
495 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I32:
496 idxa = *(guint32 *)pa;
497 idxb = *(guint32 *)pb;
498 break;
499 default:
500 g_assert_not_reached ();
502 #warning TODO : compare indicies directly, and pre-order the indexed values
503 va = (idxa > 0) ? g_ptr_array_index (base->indexed, idxa-1) : NULL;
504 vb = (idxb > 0) ? g_ptr_array_index (base->indexed, idxb-1) : NULL;
505 } else {
506 va = *((GOVal **)pa);
507 vb = *((GOVal **)pb);
510 if (f->bucketer.type != GO_VAL_BUCKET_NONE)
511 res = go_val_bucketer_apply (&f->bucketer, va) - go_val_bucketer_apply (&f->bucketer, vb);
512 else
513 res = go_val_cmp (&va, &vb);
514 if (res != 0)
515 return res;
517 return 0;
521 * go_data_cache_permute:
522 * @cache: #GODataCache
523 * @field_order: #GArray of unsigned int
524 * @permutation: #GArray of unsigned int that will be re-ordered according to the fields.
527 void
528 go_data_cache_permute (GODataCache const *cache,
529 GArray const *field_order,
530 GArray *permutation)
532 GODataCacheCompare info;
534 g_return_if_fail (IS_GO_DATA_CACHE (cache));
535 g_return_if_fail (field_order);
536 g_return_if_fail (permutation);
538 info.cache = cache;
539 info.field_order = field_order;
540 g_array_sort_with_data (permutation,
541 (GCompareDataFunc) cb_go_data_cache_cmp, &info);
544 void
545 go_data_cache_dump (GODataCache *cache,
546 GArray const *field_order,
547 GArray const *permutation)
549 GODataCacheField const *f, *base;
550 unsigned int iter, i, j, idx, num_fields;
551 gboolean index_val;
552 gpointer p;
553 GOVal *v;
555 g_return_if_fail (IS_GO_DATA_CACHE (cache));
557 num_fields = field_order ? field_order->len : cache->fields->len;
558 for (iter = 0 ; iter < cache->records_len ; iter++) {
560 if (NULL == permutation)
561 i = iter;
562 else if ((i = g_array_index (permutation, unsigned int, iter)) >= 0)
563 g_print ("[%d]", i);
564 else
565 break;
566 g_print ("%d)", iter + 1);
568 for (j = 0 ; j < num_fields ; j++) {
569 f = g_ptr_array_index (cache->fields, field_order ? g_array_index (field_order, unsigned int, j) :j);
570 base = (f->group_parent < 0) ? f : g_ptr_array_index (cache->fields, f->group_parent);
571 p = go_data_cache_records_index (cache, i) + base->offset;
572 index_val = TRUE;
573 switch (base->ref_type) {
574 case GO_DATA_CACHE_FIELD_TYPE_NONE:
575 continue;
576 case GO_DATA_CACHE_FIELD_TYPE_INLINE:
577 memcpy (&v, p, sizeof (v));
578 index_val = FALSE;
579 break;
580 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I8 : idx = *(guint8 *)p; break;
581 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I16 : idx = *(guint16 *)p; break;
582 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I32 : idx = *(guint32 *)p; break;
584 default:
585 g_warning ("unknown field type %d", base->ref_type);
586 continue;
589 if (index_val) {
590 if (idx-- == 0)
591 continue;
592 g_return_if_fail (base->indexed != NULL && idx < base->indexed->len);
594 v = g_ptr_array_index (base->indexed, idx);
595 g_print ("\t(%d) %d=", j, idx);
596 } else
597 g_print ("\t[%d] ", j);
599 if (f->bucketer.type != GO_VAL_BUCKET_NONE) {
600 int res = go_val_bucketer_apply (&f->bucketer, v);
601 go_data_cache_dump_value (g_ptr_array_index (f->grouped, res));
603 go_data_cache_dump_value (v);
605 g_print ("\n");