1 /*
2  * gog-data-set.c : A Utility interface for managing GOData as attributes
3  *
4  * Copyright (C) 2003-2004 Jody Goldberg (jody@gnome.org)
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) version 3.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
19  * USA
20  */
21 
22 #include <goffice/goffice-config.h>
23 #include <goffice/goffice.h>
24 
25 /**
26  * GogDatasetElement:
27  * @data: the #GOData
28  * @set: the owner data set.
29  * @dim_i: the dimension iside the dataset.
30  **/
31 
32 /**
33  * GogDatasetClass:
34  * @base: base class
35  * @get_elem: gets i-th element.
36  * @set_dim: sets the data for i-th element.
37  * @dims: gest first and last valid elements indices.
38  * @dim_changed: called when an element has changed.
39  **/
40 
41 GType
gog_dataset_get_type(void)42 gog_dataset_get_type (void)
43 {
44 	static GType gog_dataset_type = 0;
45 
46 	if (!gog_dataset_type) {
47 		static GTypeInfo const gog_dataset_info = {
48 			sizeof (GogDatasetClass),	/* class_size */
49 			NULL,		/* base_init */
50 			NULL,		/* base_finalize */
51 		};
52 
53 		gog_dataset_type = g_type_register_static (G_TYPE_INTERFACE,
54 			"GogDataset", &gog_dataset_info, 0);
55 	}
56 
57 	return gog_dataset_type;
58 }
59 
60 /**
61  * gog_dataset_dims:
62  * @set: #GogDataset
63  * @first: inclusive
64  * @last: _inclusive_
65  *
66  * FIXME ?? Fix what ??
67  * Stores the first and last valid indicises to get/set dim
68  * in @first and @last.
69  **/
70 void
gog_dataset_dims(GogDataset const * set,int * first,int * last)71 gog_dataset_dims (GogDataset const *set, int *first, int *last)
72 {
73 	GogDatasetClass *klass;
74 	g_return_if_fail (set);
75 	klass = GOG_DATASET_GET_CLASS (set);
76 	g_return_if_fail (klass != NULL);
77 	g_return_if_fail (first != NULL);
78 	g_return_if_fail (last != NULL);
79 	(klass->dims) (set, first, last);
80 }
81 
82 /**
83  * gog_dataset_get_dim:
84  * @set: #GogDataset
85  * @dim_i:
86  *
87  * Returns: (transfer none): the GOData associated with dimension @dim_i.  Does NOT add a
88  * 	reference.  or %NULL on failure.
89  **/
90 GOData *
gog_dataset_get_dim(GogDataset const * set,int dim_i)91 gog_dataset_get_dim (GogDataset const *set, int dim_i)
92 {
93 	GogDatasetElement *elem;
94 	g_return_val_if_fail (set, NULL);
95 	elem = gog_dataset_get_elem (set, dim_i);
96 	if (NULL == elem)
97 		return NULL;
98 	return elem->data;
99 }
100 
101 /**
102  * gog_dataset_set_dim:
103  * @set: #GogDataset
104  * @dim_i:  < 0 gets the name
105  * @val: (transfer full): #GOData
106  * @err: #GError
107  *
108  * Absorbs a ref to @val if it is non-%NULL
109  **/
110 void
gog_dataset_set_dim(GogDataset * set,int dim_i,GOData * val,GError ** err)111 gog_dataset_set_dim (GogDataset *set, int dim_i, GOData *val, GError **err)
112 {
113 	GogDatasetClass *klass;
114 	int first, last;
115 
116 	g_return_if_fail (set != NULL || val == NULL || GO_IS_DATA (val));
117 
118 	if (set == NULL || !GOG_IS_DATASET (set)) {
119 		g_warning ("gog_dataset_set_dim called with invalid GogDataset");
120 		goto done;
121 	}
122 
123 	gog_dataset_dims (set, &first, &last);
124 	if (dim_i < first || dim_i > last) {
125 		g_warning ("gog_dataset_set_dim called with invalid index (%d)", dim_i);
126 		goto done;
127 	}
128 	klass = GOG_DATASET_GET_CLASS (set);
129 
130 	/* short circuit */
131 	if (val != gog_dataset_get_dim (set, dim_i)) {
132 		gog_dataset_set_dim_internal (set, dim_i, val,
133 			gog_object_get_graph (GOG_OBJECT (set)));
134 
135 		if (klass->set_dim)
136 			(klass->set_dim) (set, dim_i, val, err);
137 		if (klass->dim_changed)
138 			(klass->dim_changed) (set, dim_i);
139 	}
140 
141 done :
142 	/* absorb ref to orig, simplifies life cycle easier for new GODatas */
143 	if (val != NULL)
144 		g_object_unref (val);
145 }
146 
147 /**
148  * gog_dataset_get_elem: (skip)
149  * @set: #GogDataset
150  * @dim_i:
151  *
152  * Returns: the GODataset associated with dimension @dim_i.
153  **/
154 GogDatasetElement *
gog_dataset_get_elem(GogDataset const * set,int dim_i)155 gog_dataset_get_elem (GogDataset const *set, int dim_i)
156 {
157 	GogDatasetClass *klass = GOG_DATASET_GET_CLASS (set);
158 	g_return_val_if_fail (klass != NULL, NULL);
159 	return (klass->get_elem) (set, dim_i);
160 }
161 
162 static void
cb_dataset_dim_changed(GOData * data,GogDatasetElement * elem)163 cb_dataset_dim_changed (GOData *data, GogDatasetElement *elem)
164 {
165 	GogDatasetClass *klass = GOG_DATASET_GET_CLASS (elem->set);
166 
167 	g_return_if_fail (klass != NULL);
168 	if (klass->dim_changed)
169 		(klass->dim_changed) (elem->set, elem->dim_i);
170 }
171 
172 /**
173  * gog_dataset_set_dim_internal:
174  * @set: #GogDataset
175  * @dim_i: the index
176  * @val: #GOData
177  * @graph: #GogGraph
178  *
179  * an internal routine to handle signal setup and teardown
180  **/
181 void
gog_dataset_set_dim_internal(GogDataset * set,int dim_i,GOData * val,GogGraph * graph)182 gog_dataset_set_dim_internal (GogDataset *set, int dim_i,
183 			      GOData *val, GogGraph *graph)
184 {
185 	GogDatasetElement *elem = gog_dataset_get_elem (set, dim_i);
186 
187 	g_return_if_fail (elem != NULL);
188 
189 	if (graph != NULL) {
190 		if (val == elem->data)
191 			return;
192 		if (val != NULL)
193 			val = gog_graph_ref_data (graph, val);
194 		if (elem->handler != 0) {
195 			g_signal_handler_disconnect (G_OBJECT (elem->data),
196 				elem->handler);
197 			elem->handler = 0;
198 			gog_graph_unref_data (graph, elem->data);
199 		}
200 		if (val != NULL)
201 			elem->handler = g_signal_connect (
202 				G_OBJECT (val), "changed",
203 				G_CALLBACK (cb_dataset_dim_changed), elem);
204 	} else {
205 		if (val != NULL)
206 			g_object_ref (val);
207 		if (elem->data != NULL)
208 			g_object_unref (elem->data);
209 	}
210 	elem->data  = val;
211 	elem->set   = set;
212 	elem->dim_i = dim_i;
213 	gog_object_request_update (GOG_OBJECT (set));
214 }
215 
216 void
gog_dataset_finalize(GogDataset * set)217 gog_dataset_finalize (GogDataset *set)
218 {
219 	GogGraph *graph = gog_object_get_graph (GOG_OBJECT (set));
220 	int first, last;
221 
222 	gog_dataset_dims (set, &first, &last);
223 	while (first <= last)
224 		gog_dataset_set_dim_internal (set, first++, NULL, graph);
225 }
226 
227 void
gog_dataset_parent_changed(GogDataset * set,gboolean was_set)228 gog_dataset_parent_changed (GogDataset *set, gboolean was_set)
229 {
230 	GogGraph *graph = gog_object_get_graph (GOG_OBJECT (set));
231 	GogDatasetElement *elem;
232 	GOData *dat;
233 	int i, last;
234 
235 	for (gog_dataset_dims (set, &i, &last); i <= last ; i++) {
236 		elem = gog_dataset_get_elem (set, i);
237 		if (elem == NULL || elem->data == NULL)
238 			continue;
239 		dat = elem->data;
240 		if (!was_set) {
241 			g_object_ref (dat);
242 			gog_dataset_set_dim_internal (set, i, NULL, graph);
243 			elem->data = dat;
244 		} else if (elem->handler == 0) {
245 			elem->data = NULL; /* disable the short circuit */
246 			gog_dataset_set_dim_internal (set, i, dat, graph);
247 			g_object_unref (dat);
248 		}
249 	}
250 	if (was_set)
251 		gog_object_request_update (GOG_OBJECT (set));
252 }
253 
254 void
gog_dataset_dup_to_simple(GogDataset const * src,GogDataset * dst)255 gog_dataset_dup_to_simple (GogDataset const *src, GogDataset *dst)
256 {
257 	gint n, last;
258 	GOData *src_dat, *dst_dat;
259 
260 	gog_dataset_dims (src, &n, &last);
261 
262 	for ( ; n <= last ; n++) {
263 		unsigned int n_dimensions;
264 
265 		src_dat = gog_dataset_get_dim (src, n);
266 		if (src_dat == NULL)
267 			continue;
268 		dst_dat = NULL;
269 
270 		n_dimensions = go_data_get_n_dimensions (src_dat);
271 
272 		/* for scalar and vector data, try to transform to values first
273 		if we find non finite, use strings */
274 
275 		switch (n_dimensions) {
276 			case 0:
277 				{
278 					char *str;
279 					char *end;
280 					double d;
281 
282 					str = go_data_get_scalar_string (src_dat);
283 					d =  g_strtod (str, &end);
284 
285 					dst_dat = (*end == 0) ?
286 						go_data_scalar_val_new (d):
287 						go_data_scalar_str_new (g_strdup (str), TRUE);
288 
289 					g_free (str);
290 				}
291 				break;
292 			case 1:
293 				{
294 					double *d;
295 					int i, n;
296 					gboolean as_values = TRUE;
297 
298 					d = go_data_get_values (src_dat);
299 					n = go_data_get_vector_size (src_dat);
300 
301 					for (i = 0; i < n; i++)
302 						if (!go_finite (d[i])) {
303 							as_values = FALSE;
304 							break;
305 						}
306 
307 					if (as_values)
308 						/* we don't need to duplicate, since this is used only for
309 						   short lived objects */
310 						dst_dat = go_data_vector_val_new (d, n, NULL);
311 					else {
312 						char **str = g_new (char*, n + 1);
313 						str[n] = NULL;
314 						for (i = 0; i < n; i++)
315 							str[i] = go_data_get_vector_string (src_dat, i);
316 
317 						dst_dat = go_data_vector_str_new ((char const* const*) str,
318 										  n, g_free);
319 					}
320 				}
321 				break;
322 			case 2:
323 				{
324 					/* only values are supported so don't care */
325 					GODataMatrixSize size;
326 
327 					go_data_get_matrix_size (src_dat, &size.rows, &size.columns);
328 
329 					dst_dat = go_data_matrix_val_new (go_data_get_values (src_dat),
330 									  size.rows, size.columns, NULL);
331 				}
332 				break;
333 			default:
334 				g_warning ("[GogDataSet::dup_to_simple] Source with invalid number of dimensions (%d)",
335 					   n_dimensions);
336 		}
337 		gog_dataset_set_dim (dst, n, dst_dat, NULL);
338 	}
339 }
340