1 /*
2 * Copyright (C) 2006 - 2008 Murray Cumming <murrayc@murrayc.com>
3 * Copyright (C) 2006 - 2012 Vivien Malerba <malerba@gnome-db.org>
4 * Copyright (C) 2009 Bas Driessen <bas.driessen@xobas.com>
5 * Copyright (C) 2010 David King <davidk@openismus.com>
6 * Copyright (C) 2010 Jonh Wendell <jwendell@gnome.org>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24 #include "gda-data-access-wrapper.h"
25
26 #include <string.h>
27 #include <glib/gi18n-lib.h>
28 #include <libgda/gda-enums.h>
29 #include <libgda/gda-data-model.h>
30 #include <libgda/gda-data-model-extra.h>
31 #include <libgda/gda-data-model-iter.h>
32 #include <libgda/gda-row.h>
33 #include <libgda/gda-holder.h>
34
35
36 #define ROWS_POOL_SIZE 50
37 struct _GdaDataAccessWrapperPrivate {
38 GdaDataModel *model;
39 GdaDataModelAccessFlags model_access_flags;
40
41 GdaDataModelIter *iter; /* iterator on @model, NULL if @model already is random access */
42 gint iter_row;/* current row of @iter, starting at 0 when created */
43
44 GHashTable *rows; /* NULL if @model already is random access */
45 gint nb_rows; /* number of rows of @wrapper */
46 gint nb_cols; /* number of columns of @wrapper */
47 gint last_row;/* row number of the last row which has been read */
48 gboolean end_of_data; /* TRUE if the end of the data model has been reached by the iterator */
49
50 GArray *rows_buffer_array; /* Array of GdaRow */
51 GArray *rows_buffer_index; /* Array of indexes: GdaRow at index i in @rows_buffer_array
52 * is indexed in @rows with key rows_buffer_index[i] */
53
54 /* rows mapping */
55 GSList *columns; /* not NULL if a mapping exists */
56 gint *rows_mapping; /* @nb_cols is set when @rows_mapping is set, and @rows_mapping's size is @nb_cols */
57 };
58
59 /* properties */
60 enum
61 {
62 PROP_0,
63 PROP_MODEL,
64 };
65
66 static void gda_data_access_wrapper_class_init (GdaDataAccessWrapperClass *klass);
67 static void gda_data_access_wrapper_init (GdaDataAccessWrapper *model,
68 GdaDataAccessWrapperClass *klass);
69 static void gda_data_access_wrapper_dispose (GObject *object);
70 static void gda_data_access_wrapper_finalize (GObject *object);
71
72 static void gda_data_access_wrapper_set_property (GObject *object,
73 guint param_id,
74 const GValue *value,
75 GParamSpec *pspec);
76 static void gda_data_access_wrapper_get_property (GObject *object,
77 guint param_id,
78 GValue *value,
79 GParamSpec *pspec);
80
81 /* GdaDataModel interface */
82 static void gda_data_access_wrapper_data_model_init (GdaDataModelIface *iface);
83 static gint gda_data_access_wrapper_get_n_rows (GdaDataModel *model);
84 static gint gda_data_access_wrapper_get_n_columns (GdaDataModel *model);
85 static GdaColumn *gda_data_access_wrapper_describe_column (GdaDataModel *model, gint col);
86 static GdaDataModelAccessFlags gda_data_access_wrapper_get_access_flags(GdaDataModel *model);
87 static const GValue *gda_data_access_wrapper_get_value_at (GdaDataModel *model, gint col, gint row, GError **error);
88 static GdaValueAttribute gda_data_access_wrapper_get_attributes_at (GdaDataModel *model, gint col, gint row);
89 static GError **gda_data_access_wrapper_get_exceptions (GdaDataModel *model);
90
91 static void iter_row_changed_cb (GdaDataModelIter *iter, gint row, GdaDataAccessWrapper *model);
92 static void iter_end_of_data_cb (GdaDataModelIter *iter, GdaDataAccessWrapper *model);
93
94 static GdaRow *create_new_row (GdaDataAccessWrapper *model);
95
96 static GObjectClass *parent_class = NULL;
97
98 /**
99 * gda_data_access_wrapper_get_type:
100 *
101 * Returns: the #GType of GdaDataAccessWrapper.
102 */
103 GType
gda_data_access_wrapper_get_type(void)104 gda_data_access_wrapper_get_type (void)
105 {
106 static GType type = 0;
107
108 if (G_UNLIKELY (type == 0)) {
109 static GMutex registering;
110 static const GTypeInfo info = {
111 sizeof (GdaDataAccessWrapperClass),
112 (GBaseInitFunc) NULL,
113 (GBaseFinalizeFunc) NULL,
114 (GClassInitFunc) gda_data_access_wrapper_class_init,
115 NULL,
116 NULL,
117 sizeof (GdaDataAccessWrapper),
118 0,
119 (GInstanceInitFunc) gda_data_access_wrapper_init,
120 0
121 };
122
123 static const GInterfaceInfo data_model_info = {
124 (GInterfaceInitFunc) gda_data_access_wrapper_data_model_init,
125 NULL,
126 NULL
127 };
128
129 g_mutex_lock (®istering);
130 if (type == 0) {
131 type = g_type_register_static (G_TYPE_OBJECT, "GdaDataAccessWrapper", &info, 0);
132 g_type_add_interface_static (type, GDA_TYPE_DATA_MODEL, &data_model_info);
133 }
134 g_mutex_unlock (®istering);
135 }
136 return type;
137 }
138
139 static void
gda_data_access_wrapper_class_init(GdaDataAccessWrapperClass * klass)140 gda_data_access_wrapper_class_init (GdaDataAccessWrapperClass *klass)
141 {
142 GObjectClass *object_class = G_OBJECT_CLASS (klass);
143
144 parent_class = g_type_class_peek_parent (klass);
145
146 /* properties */
147 object_class->set_property = gda_data_access_wrapper_set_property;
148 object_class->get_property = gda_data_access_wrapper_get_property;
149 g_object_class_install_property (object_class, PROP_MODEL,
150 g_param_spec_object ("model", NULL, "Data model being wrapped",
151 GDA_TYPE_DATA_MODEL,
152 G_PARAM_READABLE | G_PARAM_WRITABLE |
153 G_PARAM_CONSTRUCT_ONLY));
154
155 /* virtual functions */
156 object_class->dispose = gda_data_access_wrapper_dispose;
157 object_class->finalize = gda_data_access_wrapper_finalize;
158 }
159
160 static void
gda_data_access_wrapper_data_model_init(GdaDataModelIface * iface)161 gda_data_access_wrapper_data_model_init (GdaDataModelIface *iface)
162 {
163 iface->i_get_n_rows = gda_data_access_wrapper_get_n_rows;
164 iface->i_get_n_columns = gda_data_access_wrapper_get_n_columns;
165 iface->i_describe_column = gda_data_access_wrapper_describe_column;
166 iface->i_get_access_flags = gda_data_access_wrapper_get_access_flags;
167 iface->i_get_value_at = gda_data_access_wrapper_get_value_at;
168 iface->i_get_attributes_at = gda_data_access_wrapper_get_attributes_at;
169
170 iface->i_create_iter = NULL;
171 iface->i_iter_at_row = NULL;
172 iface->i_iter_next = NULL;
173 iface->i_iter_prev = NULL;
174
175 iface->i_set_value_at = NULL;
176 iface->i_iter_set_value = NULL;
177 iface->i_set_values = NULL;
178 iface->i_append_values = NULL;
179 iface->i_append_row = NULL;
180 iface->i_remove_row = NULL;
181 iface->i_find_row = NULL;
182
183 iface->i_set_notify = NULL;
184 iface->i_get_notify = NULL;
185 iface->i_send_hint = NULL;
186
187 iface->i_get_exceptions = gda_data_access_wrapper_get_exceptions;
188 }
189
190 static void
gda_data_access_wrapper_init(GdaDataAccessWrapper * model,G_GNUC_UNUSED GdaDataAccessWrapperClass * klass)191 gda_data_access_wrapper_init (GdaDataAccessWrapper *model, G_GNUC_UNUSED GdaDataAccessWrapperClass *klass)
192 {
193 g_return_if_fail (GDA_IS_DATA_ACCESS_WRAPPER (model));
194 model->priv = g_new0 (GdaDataAccessWrapperPrivate, 1);
195 model->priv->iter_row = -1; /* because model->priv->iter does not yet exist */
196 model->priv->rows = NULL;
197 model->priv->nb_rows = -1;
198 model->priv->end_of_data = FALSE;
199 model->priv->last_row = -1;
200
201 model->priv->rows_buffer_array = NULL;
202 model->priv->rows_buffer_index = NULL;
203 }
204
205 static void
model_row_inserted_cb(G_GNUC_UNUSED GdaDataModel * mod,gint row,GdaDataAccessWrapper * model)206 model_row_inserted_cb (G_GNUC_UNUSED GdaDataModel *mod, gint row, GdaDataAccessWrapper *model)
207 {
208 gda_data_model_row_inserted ((GdaDataModel*) model, row);
209 }
210
211 static void
model_row_updated_cb(G_GNUC_UNUSED GdaDataModel * mod,gint row,GdaDataAccessWrapper * model)212 model_row_updated_cb (G_GNUC_UNUSED GdaDataModel *mod, gint row, GdaDataAccessWrapper *model)
213 {
214 gda_data_model_row_updated ((GdaDataModel*) model, row);
215 }
216
217 static void
model_row_removed_cb(G_GNUC_UNUSED GdaDataModel * mod,gint row,GdaDataAccessWrapper * model)218 model_row_removed_cb (G_GNUC_UNUSED GdaDataModel *mod, gint row, GdaDataAccessWrapper *model)
219 {
220 gda_data_model_row_removed ((GdaDataModel*) model, row);
221 }
222
223 static void
model_reset_cb(G_GNUC_UNUSED GdaDataModel * mod,GdaDataAccessWrapper * model)224 model_reset_cb (G_GNUC_UNUSED GdaDataModel *mod, GdaDataAccessWrapper *model)
225 {
226 gda_data_model_reset ((GdaDataModel*) model);
227 }
228
229 static void
clear_internal_state(GdaDataAccessWrapper * model)230 clear_internal_state (GdaDataAccessWrapper *model)
231 {
232 if (model->priv) {
233 if (model->priv->columns) {
234 g_slist_foreach (model->priv->columns, (GFunc) g_object_unref, NULL);
235 g_slist_free (model->priv->columns);
236 model->priv->columns = NULL;
237 }
238
239 model->priv->nb_cols = 0;
240
241 if (model->priv->rows_buffer_array) {
242 g_array_free (model->priv->rows_buffer_array, TRUE);
243 model->priv->rows_buffer_array = NULL;
244 }
245
246 if (model->priv->rows_buffer_index) {
247 g_array_free (model->priv->rows_buffer_index, TRUE);
248 model->priv->rows_buffer_index = NULL;
249 }
250 }
251 }
252
253 static void
gda_data_access_wrapper_dispose(GObject * object)254 gda_data_access_wrapper_dispose (GObject *object)
255 {
256 GdaDataAccessWrapper *model = (GdaDataAccessWrapper *) object;
257
258 g_return_if_fail (GDA_IS_DATA_ACCESS_WRAPPER (model));
259
260 /* free memory */
261 if (model->priv) {
262 /* random access model free */
263 clear_internal_state (model);
264
265 if (model->priv->iter) {
266 g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->iter),
267 G_CALLBACK (iter_row_changed_cb), model);
268 g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->iter),
269 G_CALLBACK (iter_end_of_data_cb), model);
270
271 g_object_unref (model->priv->iter);
272 model->priv->iter = NULL;
273 }
274
275 if (model->priv->model) {
276 if (model->priv->rows) {
277 g_hash_table_destroy (model->priv->rows);
278 model->priv->rows = NULL;
279 }
280 else {
281 g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->model),
282 G_CALLBACK (model_row_inserted_cb), model);
283 g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->model),
284 G_CALLBACK (model_row_updated_cb), model);
285 g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->model),
286 G_CALLBACK (model_row_removed_cb), model);
287 g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->model),
288 G_CALLBACK (model_reset_cb), model);
289 }
290 g_object_unref (model->priv->model);
291 model->priv->model = NULL;
292 }
293 }
294
295 /* chain to parent class */
296 parent_class->dispose (object);
297 }
298
299 static void
gda_data_access_wrapper_finalize(GObject * object)300 gda_data_access_wrapper_finalize (GObject *object)
301 {
302 GdaDataAccessWrapper *model = (GdaDataAccessWrapper *) object;
303
304 g_return_if_fail (GDA_IS_DATA_ACCESS_WRAPPER (model));
305
306 /* free memory */
307 if (model->priv) {
308 g_free (model->priv);
309 model->priv = NULL;
310 }
311
312 /* chain to parent class */
313 parent_class->finalize (object);
314 }
315
316 static void
compute_columns(GdaDataAccessWrapper * model)317 compute_columns (GdaDataAccessWrapper *model)
318 {
319 if (model->priv->rows_mapping) {
320 /* use model->priv->rows_mapping to create columns, and correct it if
321 * needed to remove out of range columns */
322 gint *nmapping;
323 gint i, j, nb_cols;
324 g_assert (!model->priv->columns);
325 nmapping = g_new (gint, model->priv->nb_cols);
326 nb_cols = gda_data_model_get_n_columns (model->priv->model);
327 for (i = 0, j = 0; i < model->priv->nb_cols; i++) {
328 gint nb = model->priv->rows_mapping [i];
329 if (nb >= nb_cols)
330 continue;
331 GdaColumn *column;
332 column = gda_data_model_describe_column (model->priv->model, nb);
333 if (!column)
334 continue;
335 model->priv->columns = g_slist_append (model->priv->columns,
336 gda_column_copy (column));
337 nmapping [j] = nb;
338 j++;
339 }
340 model->priv->nb_cols = j;
341 g_free (model->priv->rows_mapping);
342 model->priv->rows_mapping = nmapping;
343 }
344 else
345 model->priv->nb_cols = gda_data_model_get_n_columns (model->priv->model);
346 }
347
348 static void
gda_data_access_wrapper_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)349 gda_data_access_wrapper_set_property (GObject *object,
350 guint param_id,
351 const GValue *value,
352 GParamSpec *pspec)
353 {
354 GdaDataAccessWrapper *model;
355
356 model = GDA_DATA_ACCESS_WRAPPER (object);
357 if (model->priv) {
358 switch (param_id) {
359 case PROP_MODEL: {
360 GdaDataModel *mod = g_value_get_object (value);
361 if (mod) {
362 g_return_if_fail (GDA_IS_DATA_MODEL (mod));
363 model->priv->model_access_flags = gda_data_model_get_access_flags (mod);
364
365 if (model->priv->model_access_flags & GDA_DATA_MODEL_ACCESS_RANDOM) {
366 g_signal_connect (G_OBJECT (mod), "row-inserted",
367 G_CALLBACK (model_row_inserted_cb), model);
368 g_signal_connect (G_OBJECT (mod), "row-updated",
369 G_CALLBACK (model_row_updated_cb), model);
370 g_signal_connect (G_OBJECT (mod), "row-removed",
371 G_CALLBACK (model_row_removed_cb), model);
372 g_signal_connect (G_OBJECT (mod), "reset",
373 G_CALLBACK (model_reset_cb), model);
374 }
375 else {
376 model->priv->iter = gda_data_model_create_iter (mod);
377 g_return_if_fail (model->priv->iter);
378 g_object_set (model->priv->iter, "validate-changes", FALSE,
379 NULL);
380 g_signal_connect (G_OBJECT (model->priv->iter), "row-changed",
381 G_CALLBACK (iter_row_changed_cb), model);
382 g_signal_connect (G_OBJECT (model->priv->iter), "end-of-data",
383 G_CALLBACK (iter_end_of_data_cb), model);
384 model->priv->iter_row = -1; /* because model->priv->iter is invalid */
385 model->priv->rows = g_hash_table_new_full (g_int_hash, g_int_equal,
386 g_free,
387 (GDestroyNotify) g_object_unref);
388 }
389
390 if (model->priv->model)
391 g_object_unref (model->priv->model);
392
393 model->priv->model = mod;
394 g_object_ref (mod);
395
396 compute_columns (model);
397 }
398 break;
399 }
400 default:
401 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
402 break;
403 }
404 }
405 }
406
407 static void
gda_data_access_wrapper_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)408 gda_data_access_wrapper_get_property (GObject *object,
409 guint param_id,
410 GValue *value,
411 GParamSpec *pspec)
412 {
413 GdaDataAccessWrapper *model;
414
415 model = GDA_DATA_ACCESS_WRAPPER (object);
416 if (model->priv) {
417 switch (param_id) {
418 case PROP_MODEL:
419 g_value_set_object (value, G_OBJECT (model->priv->model));
420 break;
421 default:
422 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
423 break;
424 }
425 }
426 }
427
428 /**
429 * gda_data_access_wrapper_new:
430 * @model: a #GdaDataModel
431 *
432 * Creates a new #GdaDataModel object which buffers the rows of @model. This object is useful
433 * only if @model can only be accessed using cursor based method.
434 *
435 * Returns: (transfer full): a pointer to the newly created #GdaDataModel.
436 */
437 GdaDataModel *
gda_data_access_wrapper_new(GdaDataModel * model)438 gda_data_access_wrapper_new (GdaDataModel *model)
439 {
440 GdaDataAccessWrapper *retmodel;
441
442 g_return_val_if_fail (GDA_IS_DATA_MODEL (model), NULL);
443
444 retmodel = g_object_new (GDA_TYPE_DATA_ACCESS_WRAPPER,
445 "model", model, NULL);
446
447 return GDA_DATA_MODEL (retmodel);
448 }
449
450 /**
451 * gda_data_access_wrapper_set_mapping:
452 * @wrapper: a #GdaDataAccessWrapper object
453 * @mapping: (allow-none) (array length=mapping_size): an array of #gint which represents the mapping between @wrapper's columns
454 * and the columns of the wrapped data model
455 * @mapping_size: the size of @mapping.
456 *
457 * @wrapper will report as many columns as @mapping_size, and for each value at position 'i' in @mapping,
458 * @wrapper will report the 'i'th column, mapped to the wrapped data model column at position mapping[i].
459 * For example if mapping is {3, 4, 0}, then @wrapper will report 3 columns, respectively mapped to the 4th,
460 * 5th and 1st columns of the wrapped data model (as column numbers start at 0).
461 *
462 * If @mapping is %NULL, then no mapping is done and @wrapper's columns will be the same as the wrapped
463 * data model.
464 *
465 * If a column in @mapping does not exist in the wrapped data model, then it is simply ignored (no error
466 * reported).
467 *
468 * Please note that if @wrapper has already been used and if the wrapped data model offers a cursor forward
469 * access mode, then this method will return %FALSE and no action will be done.
470 *
471 * If the mapping is applied, then any existing iterator will be invalid, and @wrapper is reset as if it
472 * had just been created.
473 *
474 * Returns: %TRUE if the mapping actually changed
475 *
476 * Since: 5.2
477 */
478 gboolean
gda_data_access_wrapper_set_mapping(GdaDataAccessWrapper * wrapper,const gint * mapping,gint mapping_size)479 gda_data_access_wrapper_set_mapping (GdaDataAccessWrapper *wrapper, const gint *mapping, gint mapping_size)
480 {
481 g_return_val_if_fail (GDA_IS_DATA_ACCESS_WRAPPER (wrapper), FALSE);
482
483 if ((! (wrapper->priv->model_access_flags & GDA_DATA_MODEL_ACCESS_CURSOR_BACKWARD)) &&
484 (wrapper->priv->iter_row >= 0)) {
485 /* error */
486 return FALSE;
487 }
488
489 clear_internal_state (wrapper);
490
491 if (mapping) {
492 /* define mapping */
493 wrapper->priv->rows_mapping = g_new (gint, mapping_size);
494 memcpy (wrapper->priv->rows_mapping, mapping, mapping_size * sizeof (gint));
495 wrapper->priv->nb_cols = mapping_size;
496 }
497 else {
498 if (wrapper->priv->rows_mapping) {
499 g_free (wrapper->priv->rows_mapping);
500 wrapper->priv->rows_mapping = NULL;
501 }
502 }
503
504 compute_columns (wrapper);
505 gda_data_model_reset ((GdaDataModel*) wrapper);
506
507 return TRUE;
508 }
509
510 /*
511 * GdaDataModel interface implementation
512 */
513 static gint
gda_data_access_wrapper_get_n_rows(GdaDataModel * model)514 gda_data_access_wrapper_get_n_rows (GdaDataModel *model)
515 {
516 GdaDataAccessWrapper *imodel;
517 g_return_val_if_fail (GDA_IS_DATA_ACCESS_WRAPPER (model), 0);
518 imodel = (GdaDataAccessWrapper*) model;
519 g_return_val_if_fail (imodel->priv, 0);
520
521 if (imodel->priv->nb_rows >= 0)
522 return imodel->priv->nb_rows;
523
524 if (imodel->priv->model_access_flags & GDA_DATA_MODEL_ACCESS_RANDOM)
525 /* imodel->priv->mode is a random access model, use it */
526 imodel->priv->nb_rows = gda_data_model_get_n_rows (imodel->priv->model);
527 else {
528 /* go at the end */
529 while (!imodel->priv->end_of_data) {
530 if (! gda_data_model_iter_move_next (imodel->priv->iter))
531 break;
532 }
533 if (imodel->priv->end_of_data)
534 imodel->priv->nb_rows = imodel->priv->last_row +1;
535 else
536 imodel->priv->nb_rows = -1;
537 }
538
539 return imodel->priv->nb_rows;
540 }
541
542 static gint
gda_data_access_wrapper_get_n_columns(GdaDataModel * model)543 gda_data_access_wrapper_get_n_columns (GdaDataModel *model)
544 {
545 GdaDataAccessWrapper *imodel;
546 g_return_val_if_fail (GDA_IS_DATA_ACCESS_WRAPPER (model), 0);
547 imodel = (GdaDataAccessWrapper*) model;
548 g_return_val_if_fail (imodel->priv, 0);
549
550 if (imodel->priv->model)
551 return imodel->priv->nb_cols;
552 else
553 return 0;
554 }
555
556 static GdaColumn *
gda_data_access_wrapper_describe_column(GdaDataModel * model,gint col)557 gda_data_access_wrapper_describe_column (GdaDataModel *model, gint col)
558 {
559 GdaDataAccessWrapper *imodel;
560 g_return_val_if_fail (GDA_IS_DATA_ACCESS_WRAPPER (model), NULL);
561 imodel = (GdaDataAccessWrapper*) model;
562 g_return_val_if_fail (imodel->priv, NULL);
563
564 if (imodel->priv->model) {
565 if (imodel->priv->columns)
566 return g_slist_nth_data (imodel->priv->columns, col);
567 else
568 return gda_data_model_describe_column (imodel->priv->model, col);
569 }
570 else
571 return NULL;
572 }
573
574 static GdaDataModelAccessFlags
gda_data_access_wrapper_get_access_flags(GdaDataModel * model)575 gda_data_access_wrapper_get_access_flags (GdaDataModel *model)
576 {
577 GdaDataAccessWrapper *imodel;
578
579 g_return_val_if_fail (GDA_IS_DATA_ACCESS_WRAPPER (model), 0);
580 imodel = (GdaDataAccessWrapper*) model;
581 g_return_val_if_fail (imodel->priv, 0);
582
583 return GDA_DATA_MODEL_ACCESS_RANDOM;
584 }
585
586 static GdaRow *
create_new_row(GdaDataAccessWrapper * model)587 create_new_row (GdaDataAccessWrapper *model)
588 {
589 gint i;
590 GdaRow *row;
591
592 row = gda_row_new (model->priv->nb_cols);
593 for (i = 0; i < model->priv->nb_cols; i++) {
594 GdaHolder *holder;
595 GValue *dest;
596 dest = gda_row_get_value (row, i);
597 if (model->priv->rows_mapping)
598 holder = gda_set_get_nth_holder ((GdaSet *) model->priv->iter, model->priv->rows_mapping [i]);
599 else
600 holder = gda_set_get_nth_holder ((GdaSet *) model->priv->iter, i);
601 if (holder) {
602 const GValue *cvalue = gda_holder_get_value (holder);
603 if (cvalue) {
604 gda_value_reset_with_type (dest, G_VALUE_TYPE ((GValue *) cvalue));
605 g_value_copy (cvalue, dest);
606 }
607 else
608 gda_value_set_null (dest);
609 }
610 else
611 gda_row_invalidate_value (row, dest);
612 }
613
614 gint *ptr;
615 ptr = g_new (gint, 1);
616 *ptr = model->priv->iter_row;
617 g_hash_table_insert (model->priv->rows, ptr, row);
618 /*g_print ("%s(%d)\n", __FUNCTION__, model->priv->iter_row);*/
619
620 return row;
621 }
622
623 static const GValue *
gda_data_access_wrapper_get_value_at(GdaDataModel * model,gint col,gint row,GError ** error)624 gda_data_access_wrapper_get_value_at (GdaDataModel *model, gint col, gint row, GError **error)
625 {
626 GdaDataAccessWrapper *imodel;
627
628 g_return_val_if_fail (GDA_IS_DATA_ACCESS_WRAPPER (model), NULL);
629 imodel = (GdaDataAccessWrapper*) model;
630 g_return_val_if_fail (imodel->priv, NULL);
631 g_return_val_if_fail (imodel->priv->model, NULL);
632 g_return_val_if_fail (row >= 0, NULL);
633
634 if (col >= imodel->priv->nb_cols) {
635 g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_COLUMN_OUT_OF_RANGE_ERROR,
636 _("Column %d out of range (0-%d)"), col, imodel->priv->nb_cols - 1);
637 return NULL;
638 }
639
640 if (!imodel->priv->rows) {
641 /* imodel->priv->model is a random access model, use it */
642 if (imodel->priv->rows_mapping)
643 return gda_data_model_get_value_at (imodel->priv->model, imodel->priv->rows_mapping [col],
644 row, error);
645 else
646 return gda_data_model_get_value_at (imodel->priv->model, col, row, error);
647 }
648 else {
649 GdaRow *gda_row;
650 gint tmp;
651 tmp = row;
652 gda_row = g_hash_table_lookup (imodel->priv->rows, &tmp);
653 if (gda_row) {
654 GValue *val = gda_row_get_value (gda_row, col);
655 if (gda_row_value_is_valid (gda_row, val))
656 return val;
657 else
658 return NULL;
659 }
660 else {
661 g_assert (imodel->priv->iter);
662 if (imodel->priv->iter_row < 0) {
663 if (gda_data_model_iter_move_next (imodel->priv->iter)) {
664 tmp = row;
665 gda_row = g_hash_table_lookup (imodel->priv->rows, &tmp);
666 if (row == imodel->priv->iter_row) {
667 GValue *val = gda_row_get_value (gda_row, col);
668 if (gda_row_value_is_valid (gda_row, val))
669 return val;
670 else
671 return NULL;
672 }
673 }
674 else {
675 g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR,
676 "%s", _("Can't set iterator's position"));
677 return NULL;
678 }
679 }
680
681 gda_row = NULL;
682 if (row != imodel->priv->iter_row) {
683 if (row > imodel->priv->iter_row) {
684 /* need to move forward */
685 while ((imodel->priv->iter_row < row) &&
686 gda_data_model_iter_move_next (imodel->priv->iter));
687 }
688 else {
689 /* need to move backwards */
690 g_assert (imodel->priv->model_access_flags & GDA_DATA_MODEL_ACCESS_CURSOR_BACKWARD);
691 while ((imodel->priv->iter_row > row) &&
692 gda_data_model_iter_move_prev (imodel->priv->iter)) ;
693 }
694 }
695
696 if (! (imodel->priv->model_access_flags & GDA_DATA_MODEL_ACCESS_CURSOR_BACKWARD) ||
697 ! (imodel->priv->model_access_flags & GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD)) {
698 tmp = row;
699 gda_row = g_hash_table_lookup (imodel->priv->rows, &tmp);
700
701 if (gda_row) {
702 GValue *val = gda_row_get_value (gda_row, col);
703 if (gda_row_value_is_valid (gda_row, val))
704 return val;
705 else
706 return NULL;
707 }
708 }
709 else {
710 /* in this case iter can be moved forward and backward at will => we only
711 * need to keep a pool of GdaRow for performances reasons */
712 tmp = row;
713 gda_row = g_hash_table_lookup (imodel->priv->rows, &tmp);
714
715 if (!gda_row) {
716 if (! imodel->priv->rows_buffer_array) {
717 imodel->priv->rows_buffer_array = g_array_sized_new (FALSE, FALSE,
718 sizeof (GdaRow*),
719 ROWS_POOL_SIZE);
720 imodel->priv->rows_buffer_index = g_array_sized_new (FALSE, FALSE,
721 sizeof (gint),
722 ROWS_POOL_SIZE);
723 }
724 else if (imodel->priv->rows_buffer_array->len == ROWS_POOL_SIZE) {
725 /* get rid of the oldest row (was model's index_row row)*/
726 gint index_row;
727
728 index_row = g_array_index (imodel->priv->rows_buffer_index, gint,
729 ROWS_POOL_SIZE - 1);
730 g_array_remove_index (imodel->priv->rows_buffer_array,
731 ROWS_POOL_SIZE - 1);
732 g_array_remove_index (imodel->priv->rows_buffer_index,
733 ROWS_POOL_SIZE - 1);
734 g_hash_table_remove (imodel->priv->rows, &index_row);
735 }
736 if (gda_data_model_iter_move_to_row (imodel->priv->iter, row)) {
737 gda_row = create_new_row (imodel);
738 g_array_prepend_val (imodel->priv->rows_buffer_array, gda_row);
739 g_array_prepend_val (imodel->priv->rows_buffer_index, imodel->priv->iter_row);
740 }
741 }
742
743 GValue *val;
744 val = gda_row ? gda_row_get_value (gda_row, col) : NULL;
745 if (gda_row && gda_row_value_is_valid (gda_row, val))
746 return val;
747 else
748 return NULL;
749 }
750 }
751 }
752
753 g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR,
754 "%s", _("Can't access data"));
755 return NULL;
756 }
757
758 static void
iter_row_changed_cb(GdaDataModelIter * iter,gint row,GdaDataAccessWrapper * model)759 iter_row_changed_cb (GdaDataModelIter *iter, gint row, GdaDataAccessWrapper *model)
760 {
761 g_assert (model->priv->rows);
762
763 /*g_print ("%s(%d)\n", __FUNCTION__, row);*/
764 if (gda_data_model_iter_is_valid (iter)) {
765 model->priv->iter_row = row;
766 if (model->priv->last_row < row)
767 model->priv->last_row = row;
768
769 if (! (model->priv->model_access_flags & GDA_DATA_MODEL_ACCESS_CURSOR_BACKWARD) ||
770 ! (model->priv->model_access_flags & GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD)) {
771 /* keep the changes in rows */
772 GdaRow *gda_row;
773 gint tmp;
774 tmp = row;
775 gda_row = g_hash_table_lookup (model->priv->rows, &tmp);
776 if (!gda_row)
777 create_new_row (model);
778 }
779 }
780 }
781
782 static void
iter_end_of_data_cb(G_GNUC_UNUSED GdaDataModelIter * iter,GdaDataAccessWrapper * model)783 iter_end_of_data_cb (G_GNUC_UNUSED GdaDataModelIter *iter, GdaDataAccessWrapper *model)
784 {
785 g_assert (GDA_IS_DATA_ACCESS_WRAPPER (model));
786 model->priv->end_of_data = TRUE;
787 }
788
789 static GdaValueAttribute
gda_data_access_wrapper_get_attributes_at(GdaDataModel * model,gint col,gint row)790 gda_data_access_wrapper_get_attributes_at (GdaDataModel *model, gint col, gint row)
791 {
792 GdaValueAttribute flags = 0;
793 GdaDataAccessWrapper *imodel;
794
795 g_return_val_if_fail (GDA_IS_DATA_ACCESS_WRAPPER (model), 0);
796 imodel = (GdaDataAccessWrapper *) model;
797 g_return_val_if_fail (imodel->priv, 0);
798
799 if (imodel->priv->model) {
800 if (imodel->priv->rows_mapping)
801 flags = gda_data_model_get_attributes_at (imodel->priv->model, imodel->priv->rows_mapping [col],
802 row);
803 else
804 flags = gda_data_model_get_attributes_at (imodel->priv->model, col, row);
805 }
806
807 flags |= GDA_VALUE_ATTR_NO_MODIF;
808
809 return flags;
810 }
811
812 static GError **
gda_data_access_wrapper_get_exceptions(GdaDataModel * model)813 gda_data_access_wrapper_get_exceptions (GdaDataModel *model)
814 {
815 GdaDataAccessWrapper *imodel;
816
817 g_return_val_if_fail (GDA_IS_DATA_ACCESS_WRAPPER (model), NULL);
818 imodel = (GdaDataAccessWrapper *) model;
819 g_return_val_if_fail (imodel->priv, NULL);
820 return gda_data_model_get_exceptions (imodel->priv->model);
821 }
822