1 /* -*- mode: C -*- */
2 /*
3 IGraph library.
4 Copyright (C) 2005-2012 Gabor Csardi <csardi.gabor@gmail.com>
5 334 Harvard street, Cambridge, MA 02139 USA
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
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.
16
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 Street, Fifth Floor, Boston, MA
20 02110-1301 USA
21
22 */
23
24 #include "igraph_attributes.h"
25 #include "igraph_memory.h"
26
27 #include "graph/attributes.h"
28
29 #include "config.h"
30
31 #include <string.h>
32 #include <stdarg.h>
33
34 /* Should you ever want to have a thread-local attribute handler table, prepend
35 * IGRAPH_THREAD_LOCAL to the following declaration */
36 igraph_attribute_table_t *igraph_i_attribute_table = 0;
37
igraph_i_attribute_init(igraph_t * graph,void * attr)38 int igraph_i_attribute_init(igraph_t *graph, void *attr) {
39 graph->attr = 0;
40 if (igraph_i_attribute_table) {
41 return igraph_i_attribute_table->init(graph, attr);
42 } else {
43 return 0;
44 }
45 }
46
igraph_i_attribute_destroy(igraph_t * graph)47 void igraph_i_attribute_destroy(igraph_t *graph) {
48 if (igraph_i_attribute_table) {
49 igraph_i_attribute_table->destroy(graph);
50 }
51 }
52
igraph_i_attribute_copy(igraph_t * to,const igraph_t * from,igraph_bool_t ga,igraph_bool_t va,igraph_bool_t ea)53 int igraph_i_attribute_copy(igraph_t *to, const igraph_t *from, igraph_bool_t ga,
54 igraph_bool_t va, igraph_bool_t ea) {
55 if (igraph_i_attribute_table) {
56 return igraph_i_attribute_table->copy(to, from, ga, va, ea);
57 } else {
58 return 0;
59 }
60 }
61
igraph_i_attribute_add_vertices(igraph_t * graph,long int nv,void * attr)62 int igraph_i_attribute_add_vertices(igraph_t *graph, long int nv, void *attr) {
63 if (igraph_i_attribute_table) {
64 return igraph_i_attribute_table->add_vertices(graph, nv, attr);
65 } else {
66 return 0;
67 }
68 }
69
igraph_i_attribute_permute_vertices(const igraph_t * graph,igraph_t * newgraph,const igraph_vector_t * idx)70 int igraph_i_attribute_permute_vertices(const igraph_t *graph,
71 igraph_t *newgraph,
72 const igraph_vector_t *idx) {
73
74 if (igraph_i_attribute_table) {
75 return igraph_i_attribute_table->permute_vertices(graph, newgraph, idx);
76 } else {
77 return 0;
78 }
79 }
80
igraph_i_attribute_combine_vertices(const igraph_t * graph,igraph_t * newgraph,const igraph_vector_ptr_t * merges,const igraph_attribute_combination_t * comb)81 int igraph_i_attribute_combine_vertices(const igraph_t *graph,
82 igraph_t *newgraph,
83 const igraph_vector_ptr_t *merges,
84 const igraph_attribute_combination_t *comb) {
85 if (igraph_i_attribute_table) {
86 return igraph_i_attribute_table->combine_vertices(graph, newgraph,
87 merges,
88 comb);
89 } else {
90 return 0;
91 }
92 }
93
igraph_i_attribute_add_edges(igraph_t * graph,const igraph_vector_t * edges,void * attr)94 int igraph_i_attribute_add_edges(igraph_t *graph,
95 const igraph_vector_t *edges, void *attr) {
96 if (igraph_i_attribute_table) {
97 return igraph_i_attribute_table->add_edges(graph, edges, attr);
98 } else {
99 return 0;
100 }
101 }
102
igraph_i_attribute_permute_edges(const igraph_t * graph,igraph_t * newgraph,const igraph_vector_t * idx)103 int igraph_i_attribute_permute_edges(const igraph_t *graph,
104 igraph_t *newgraph,
105 const igraph_vector_t *idx) {
106 if (igraph_i_attribute_table) {
107 return igraph_i_attribute_table->permute_edges(graph, newgraph, idx);
108 } else {
109 return 0;
110 }
111 }
112
igraph_i_attribute_combine_edges(const igraph_t * graph,igraph_t * newgraph,const igraph_vector_ptr_t * merges,const igraph_attribute_combination_t * comb)113 int igraph_i_attribute_combine_edges(const igraph_t *graph,
114 igraph_t *newgraph,
115 const igraph_vector_ptr_t *merges,
116 const igraph_attribute_combination_t *comb) {
117 if (igraph_i_attribute_table) {
118 return igraph_i_attribute_table->combine_edges(graph, newgraph,
119 merges,
120 comb);
121 } else {
122 return 0;
123 }
124 }
125
igraph_i_attribute_get_info(const igraph_t * graph,igraph_strvector_t * gnames,igraph_vector_t * gtypes,igraph_strvector_t * vnames,igraph_vector_t * vtypes,igraph_strvector_t * enames,igraph_vector_t * etypes)126 int igraph_i_attribute_get_info(const igraph_t *graph,
127 igraph_strvector_t *gnames,
128 igraph_vector_t *gtypes,
129 igraph_strvector_t *vnames,
130 igraph_vector_t *vtypes,
131 igraph_strvector_t *enames,
132 igraph_vector_t *etypes) {
133 if (igraph_i_attribute_table) {
134 return igraph_i_attribute_table->get_info(graph, gnames, gtypes,
135 vnames, vtypes,
136 enames, etypes);
137 } else {
138 return 0;
139 }
140 }
141
igraph_i_attribute_has_attr(const igraph_t * graph,igraph_attribute_elemtype_t type,const char * name)142 igraph_bool_t igraph_i_attribute_has_attr(const igraph_t *graph,
143 igraph_attribute_elemtype_t type,
144 const char *name) {
145 if (igraph_i_attribute_table) {
146 return igraph_i_attribute_table->has_attr(graph, type, name);
147 } else {
148 return 0;
149 }
150 }
151
igraph_i_attribute_gettype(const igraph_t * graph,igraph_attribute_type_t * type,igraph_attribute_elemtype_t elemtype,const char * name)152 int igraph_i_attribute_gettype(const igraph_t *graph,
153 igraph_attribute_type_t *type,
154 igraph_attribute_elemtype_t elemtype,
155 const char *name) {
156 if (igraph_i_attribute_table) {
157 return igraph_i_attribute_table->gettype(graph, type, elemtype, name);
158 } else {
159 return 0;
160 }
161
162 }
163
igraph_i_attribute_get_numeric_graph_attr(const igraph_t * graph,const char * name,igraph_vector_t * value)164 int igraph_i_attribute_get_numeric_graph_attr(const igraph_t *graph,
165 const char *name,
166 igraph_vector_t *value) {
167 if (igraph_i_attribute_table) {
168 return igraph_i_attribute_table->get_numeric_graph_attr(graph, name, value);
169 } else {
170 return 0;
171 }
172 }
173
igraph_i_attribute_get_numeric_vertex_attr(const igraph_t * graph,const char * name,igraph_vs_t vs,igraph_vector_t * value)174 int igraph_i_attribute_get_numeric_vertex_attr(const igraph_t *graph,
175 const char *name,
176 igraph_vs_t vs,
177 igraph_vector_t *value) {
178 if (igraph_i_attribute_table) {
179 return igraph_i_attribute_table->get_numeric_vertex_attr(graph, name, vs, value);
180 } else {
181 return 0;
182 }
183 }
184
igraph_i_attribute_get_numeric_edge_attr(const igraph_t * graph,const char * name,igraph_es_t es,igraph_vector_t * value)185 int igraph_i_attribute_get_numeric_edge_attr(const igraph_t *graph,
186 const char *name,
187 igraph_es_t es,
188 igraph_vector_t *value) {
189 if (igraph_i_attribute_table) {
190 return igraph_i_attribute_table->get_numeric_edge_attr(graph, name, es, value);
191 } else {
192 return 0;
193 }
194 }
195
igraph_i_attribute_get_string_graph_attr(const igraph_t * graph,const char * name,igraph_strvector_t * value)196 int igraph_i_attribute_get_string_graph_attr(const igraph_t *graph,
197 const char *name,
198 igraph_strvector_t *value) {
199 if (igraph_i_attribute_table) {
200 return igraph_i_attribute_table->get_string_graph_attr(graph, name, value);
201 } else {
202 return 0;
203 }
204 }
205
igraph_i_attribute_get_string_vertex_attr(const igraph_t * graph,const char * name,igraph_vs_t vs,igraph_strvector_t * value)206 int igraph_i_attribute_get_string_vertex_attr(const igraph_t *graph,
207 const char *name,
208 igraph_vs_t vs,
209 igraph_strvector_t *value) {
210 if (igraph_i_attribute_table) {
211 return igraph_i_attribute_table->get_string_vertex_attr(graph, name, vs, value);
212 } else {
213 return 0;
214 }
215 }
216
igraph_i_attribute_get_string_edge_attr(const igraph_t * graph,const char * name,igraph_es_t es,igraph_strvector_t * value)217 int igraph_i_attribute_get_string_edge_attr(const igraph_t *graph,
218 const char *name,
219 igraph_es_t es,
220 igraph_strvector_t *value) {
221 if (igraph_i_attribute_table) {
222 return igraph_i_attribute_table->get_string_edge_attr(graph, name, es, value);
223 } else {
224 return 0;
225 }
226 }
227
igraph_i_attribute_get_bool_graph_attr(const igraph_t * graph,const char * name,igraph_vector_bool_t * value)228 int igraph_i_attribute_get_bool_graph_attr(const igraph_t *graph,
229 const char *name,
230 igraph_vector_bool_t *value) {
231 if (igraph_i_attribute_table) {
232 return igraph_i_attribute_table->get_bool_graph_attr(graph, name, value);
233 } else {
234 return 0;
235 }
236 }
237
igraph_i_attribute_get_bool_vertex_attr(const igraph_t * graph,const char * name,igraph_vs_t vs,igraph_vector_bool_t * value)238 int igraph_i_attribute_get_bool_vertex_attr(const igraph_t *graph,
239 const char *name,
240 igraph_vs_t vs,
241 igraph_vector_bool_t *value) {
242 if (igraph_i_attribute_table) {
243 return igraph_i_attribute_table->get_bool_vertex_attr(graph, name, vs, value);
244 } else {
245 return 0;
246 }
247 }
248
igraph_i_attribute_get_bool_edge_attr(const igraph_t * graph,const char * name,igraph_es_t es,igraph_vector_bool_t * value)249 int igraph_i_attribute_get_bool_edge_attr(const igraph_t *graph,
250 const char *name,
251 igraph_es_t es,
252 igraph_vector_bool_t *value) {
253 if (igraph_i_attribute_table) {
254 return igraph_i_attribute_table->get_bool_edge_attr(graph, name, es, value);
255 } else {
256 return 0;
257 }
258 }
259
260 /**
261 * \function igraph_set_attribute_table
262 * \brief Attach an attribute table.
263 *
264 * This function attaches attribute handling code to the igraph library.
265 * Note that the attribute handler table is \em not thread-local even if
266 * igraph is compiled in thread-local mode. In the vast majority of cases,
267 * this is not a significant restriction.
268 *
269 * \param table Pointer to an \ref igraph_attribute_table_t object
270 * containing the functions for attribute manipulation. Supply \c
271 * NULL here if you don't want attributes.
272 * \return Pointer to the old attribute handling table.
273 *
274 * Time complexity: O(1).
275 */
276
277 igraph_attribute_table_t *
igraph_set_attribute_table(const igraph_attribute_table_t * table)278 igraph_set_attribute_table(const igraph_attribute_table_t * table) {
279 igraph_attribute_table_t *old = igraph_i_attribute_table;
280 igraph_i_attribute_table = (igraph_attribute_table_t*) table;
281 return old;
282 }
283
284 igraph_attribute_table_t *
igraph_i_set_attribute_table(const igraph_attribute_table_t * table)285 igraph_i_set_attribute_table(const igraph_attribute_table_t * table) {
286 IGRAPH_WARNING("igraph_i_set_attribute_table is deprecated, use igraph_set_attribute_table.");
287 return igraph_set_attribute_table(table);
288 }
289
igraph_has_attribute_table()290 igraph_bool_t igraph_has_attribute_table() {
291 return igraph_i_attribute_table != 0;
292 }
293
294
295 /**
296 * \function igraph_attribute_combination_init
297 * \brief Initialize attribute combination list.
298 *
299 * \param comb The uninitialized attribute combination list.
300 * \return Error code.
301 *
302 * Time complexity: O(1)
303 */
igraph_attribute_combination_init(igraph_attribute_combination_t * comb)304 int igraph_attribute_combination_init(igraph_attribute_combination_t *comb) {
305 IGRAPH_CHECK(igraph_vector_ptr_init(&comb->list, 0));
306 return IGRAPH_SUCCESS;
307 }
308
309 /**
310 * \function igraph_attribute_combination_destroy
311 * \brief Destroy attribute combination list.
312 *
313 * \param comb The attribute combination list.
314 *
315 * Time complexity: O(n), where n is the number of records in the
316 attribute combination list.
317 */
igraph_attribute_combination_destroy(igraph_attribute_combination_t * comb)318 void igraph_attribute_combination_destroy(igraph_attribute_combination_t *comb) {
319 long int i, n = igraph_vector_ptr_size(&comb->list);
320 for (i = 0; i < n; i++) {
321 igraph_attribute_combination_record_t *rec = VECTOR(comb->list)[i];
322 if (rec->name) {
323 IGRAPH_FREE(rec->name);
324 }
325 IGRAPH_FREE(rec);
326 }
327 igraph_vector_ptr_destroy(&comb->list);
328 }
329
330 /**
331 * \function igraph_attribute_combination_add
332 * \brief Add combination record to attribute combination list.
333 *
334 * \param comb The attribute combination list.
335 * \param name The name of the attribute. If the name already exists
336 * the attribute combination record will be replaced.
337 * Use NULL to add a default combination record for all
338 * atributes not in the list.
339 * \param type The type of the attribute combination. See \ref
340 * igraph_attribute_combination_type_t for the options.
341 * \param func Function to be used if \p type is
342 * \c IGRAPH_ATTRIBUTE_COMBINE_FUNCTION.
343 * \return Error code.
344 *
345 * Time complexity: O(n), where n is the number of current attribute
346 * combinations.
347 */
igraph_attribute_combination_add(igraph_attribute_combination_t * comb,const char * name,igraph_attribute_combination_type_t type,igraph_function_pointer_t func)348 int igraph_attribute_combination_add(igraph_attribute_combination_t *comb,
349 const char *name,
350 igraph_attribute_combination_type_t type,
351 igraph_function_pointer_t func) {
352 long int i, n = igraph_vector_ptr_size(&comb->list);
353
354 /* Search, in case it is already there */
355 for (i = 0; i < n; i++) {
356 igraph_attribute_combination_record_t *r = VECTOR(comb->list)[i];
357 const char *n = r->name;
358 if ( (!name && !n) ||
359 (name && n && !strcmp(n, name)) ) {
360 r->type = type;
361 r->func = func;
362 break;
363 }
364 }
365
366 if (i == n) {
367 /* This is a new attribute name */
368 igraph_attribute_combination_record_t *rec =
369 IGRAPH_CALLOC(1, igraph_attribute_combination_record_t);
370
371 if (!rec) {
372 IGRAPH_ERROR("Cannot create attribute combination data",
373 IGRAPH_ENOMEM);
374 }
375 if (!name) {
376 rec->name = NULL;
377 } else {
378 rec->name = strdup(name);
379 }
380 rec->type = type;
381 rec->func = func;
382
383 IGRAPH_CHECK(igraph_vector_ptr_push_back(&comb->list, rec));
384
385 }
386
387 return IGRAPH_SUCCESS;
388 }
389
390 /**
391 * \function igraph_attribute_combination_remove
392 * \brief Remove a record from an attribute combination list.
393 *
394 * \param comb The attribute combination list.
395 * \param name The attribute name of the attribute combination record
396 * to remove. It will be ignored if the named attribute
397 * does not exist. It can be NULL to remove the default
398 * combination record.
399 * \return Error code. This currently always returns IGRAPH_SUCCESS.
400 *
401 * Time complexity: O(n), where n is the number of records in the attribute
402 combination list.
403 */
igraph_attribute_combination_remove(igraph_attribute_combination_t * comb,const char * name)404 int igraph_attribute_combination_remove(igraph_attribute_combination_t *comb,
405 const char *name) {
406 long int i, n = igraph_vector_ptr_size(&comb->list);
407
408 /* Search, in case it is already there */
409 for (i = 0; i < n; i++) {
410 igraph_attribute_combination_record_t *r = VECTOR(comb->list)[i];
411 const char *n = r->name;
412 if ( (!name && !n) ||
413 (name && n && !strcmp(n, name)) ) {
414 break;
415 }
416 }
417
418 if (i != n) {
419 igraph_attribute_combination_record_t *r = VECTOR(comb->list)[i];
420 if (r->name) {
421 IGRAPH_FREE(r->name);
422 }
423 IGRAPH_FREE(r);
424 igraph_vector_ptr_remove(&comb->list, i);
425 } else {
426 /* It is not there, we don't do anything */
427 }
428
429 return IGRAPH_SUCCESS;
430 }
431
igraph_attribute_combination_query(const igraph_attribute_combination_t * comb,const char * name,igraph_attribute_combination_type_t * type,igraph_function_pointer_t * func)432 int igraph_attribute_combination_query(const igraph_attribute_combination_t *comb,
433 const char *name,
434 igraph_attribute_combination_type_t *type,
435 igraph_function_pointer_t *func) {
436 long int i, def = -1, len = igraph_vector_ptr_size(&comb->list);
437
438 for (i = 0; i < len; i++) {
439 igraph_attribute_combination_record_t *rec = VECTOR(comb->list)[i];
440 const char *n = rec->name;
441 if ( (!name && !n) ||
442 (name && n && !strcmp(n, name)) ) {
443 *type = rec->type;
444 *func = rec->func;
445 return 0;
446 }
447 if (!n) {
448 def = i;
449 }
450 }
451
452 if (def == -1) {
453 /* Did not find anything */
454 *type = IGRAPH_ATTRIBUTE_COMBINE_DEFAULT;
455 *func = 0;
456 } else {
457 igraph_attribute_combination_record_t *rec = VECTOR(comb->list)[def];
458 *type = rec->type;
459 *func = rec->func;
460 }
461
462 return 0;
463 }
464
igraph_attribute_combination(igraph_attribute_combination_t * comb,...)465 int igraph_attribute_combination(igraph_attribute_combination_t *comb, ...) {
466
467 va_list ap;
468
469 IGRAPH_CHECK(igraph_attribute_combination_init(comb));
470
471 va_start(ap, comb);
472 while (1) {
473 igraph_function_pointer_t func = 0;
474 igraph_attribute_combination_type_t type;
475 const char *name;
476
477 name = va_arg(ap, const char *);
478
479 if (name == IGRAPH_NO_MORE_ATTRIBUTES) {
480 break;
481 }
482
483 type = (igraph_attribute_combination_type_t)va_arg(ap, int);
484 if (type == IGRAPH_ATTRIBUTE_COMBINE_FUNCTION) {
485 func = va_arg(ap, igraph_function_pointer_t);
486 }
487
488 if (strlen(name) == 0) {
489 name = 0;
490 }
491
492 IGRAPH_CHECK(igraph_attribute_combination_add(comb, name, type, func));
493 }
494
495 va_end(ap);
496
497 return 0;
498 }
499