source: branches/stable/ARBDB/aditem.cxx

Last change on this file was 18341, checked in by westram, 4 years ago
  • temporary workaround for broken SINA binary compatibility (has been broken by r18159).
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.3 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : aditem.cxx                                        //
4//   Purpose   : item functions                                    //
5//               items are e.g. species, SAIs, genes, etc          //
6//                                                                 //
7//   Institute of Microbiology (Technical University Munich)       //
8//   http://www.arb-home.de/                                       //
9//                                                                 //
10// =============================================================== //
11
12#include "gb_local.h"
13
14#include <arbdbt.h>
15#include <arb_strbuf.h>
16
17
18GBDATA *GBT_find_or_create_item_rel_item_data(GBDATA *gb_item_data, const char *itemname, const char *id_field, const char *id, bool markCreated) {
19    /*! Search for an existing or create a new, named item.
20     * @param gb_item_data  item container
21     * @param itemname      description of itemtype (for error messages)
22     * @param id_field      item-field containing ID (e.g. "name")
23     * @param id            the ID itself (compare is case-insensitive)
24     * @param markCreated   true -> mark item if it was created
25     * @return found/created item or NULp if an error occurs (which is exported in that case)
26     */
27
28    GBDATA   *gb_item = NULp;
29    GB_ERROR  error   = NULp;
30
31    if (!gb_item_data) error = "Missing parent container";
32    else {
33        gb_item = GBT_find_item_rel_item_data(gb_item_data, id_field, id);
34        if (!gb_item) {
35            error = GB_push_transaction(gb_item_data);
36            if (!error) {
37                gb_item             = GB_create_container(gb_item_data, itemname); // create a new item
38                if (!gb_item) error = GB_await_error();
39                else {
40                    error = GBT_write_string(gb_item, id_field, id); // write item identifier
41                    if (!error && markCreated) GB_write_flag(gb_item, 1); // mark generated item
42                }
43            }
44            error = GB_end_transaction(gb_item_data, error);
45        }
46    }
47
48    if (!gb_item && !error) error = GB_await_error();
49    if (error) {
50        gb_item = NULp;
51        GB_export_errorf("Can't create %s '%s': %s", itemname, id, error);
52    }
53
54    return gb_item;
55}
56
57GBDATA *GBT_find_or_create_species_rel_species_data(GBDATA *gb_species_data, const char *name, bool markCreated) {
58    return GBT_find_or_create_item_rel_item_data(gb_species_data, "species", "name", name, markCreated);
59}
60
61GBDATA *GBT_find_or_create_species(GBDATA *gb_main, const char *name, bool markCreated) {
62    return GBT_find_or_create_species_rel_species_data(GBT_get_species_data(gb_main), name, markCreated);
63}
64
65GBDATA *GBT_find_or_create_SAI(GBDATA *gb_main, const char *name) {
66    //! Search for an SAI, when SAI does not exist, create it
67    return GBT_find_or_create_item_rel_item_data(GBT_get_SAI_data(gb_main), "extended", "name", name, false);
68}
69
70
71// ------------------------------------
72//      some simple find procedures
73
74GBDATA *GBT_find_item_rel_item_data(GBDATA *gb_item_data, const char *id_field, const char *id_value) {
75    /*! search for items starting at item container
76     *
77     * @param 'gb_item_data' is a container containing items
78     * @param 'id_field' is a field containing a unique identifier for each item (e.g. 'name' for species)
79     * @param 'id_value' expected value of 'id_field'
80     *
81     * @return a pointer to an item with 'id_field' containing 'id_value'
82     * or NULp (in this case an error MAY be exported)
83     *
84     * Note: If you expect the item to exist, use GBT_expect_item_rel_item_data()
85     */
86
87    GBDATA *gb_item_id = GB_find_string(gb_item_data, id_field, id_value, GB_IGNORE_CASE, SEARCH_GRANDCHILD);
88    return gb_item_id ? GB_get_father(gb_item_id) : NULp;
89}
90
91GBDATA *GBT_expect_item_rel_item_data(GBDATA *gb_item_data, const char *id_field, const char *id_value) {
92    //! like GBT_find_item_rel_item_data(), but also exports an error if the item is not present
93
94    GBDATA *gb_found = GBT_find_item_rel_item_data(gb_item_data, id_field, id_value);
95    if (!gb_found && !GB_have_error()) { // item simply not exists
96        GBDATA     *gb_any   = GB_find(gb_item_data, id_field, SEARCH_GRANDCHILD);
97        const char *itemname = gb_any ? GB_read_key_pntr(GB_get_father(gb_any)) : "<item>";
98        GB_export_errorf("Could not find %s with %s '%s'", itemname, id_field, id_value);
99    }
100    return gb_found;
101}
102
103// --------------------------------------------------------------------------------
104
105GBDATA *GBT_get_species_data(GBDATA *gb_main) {
106    return GBT_find_or_create(gb_main, "species_data", 7);
107}
108
109GBDATA *GBT_first_marked_species_rel_species_data(GBDATA *gb_species_data) {
110    return GB_first_marked(gb_species_data, "species");
111}
112
113GBDATA *GBT_first_marked_species(GBDATA *gb_main) {
114    return GB_first_marked(GBT_get_species_data(gb_main), "species");
115}
116GBDATA *GBT_next_marked_species(GBDATA *gb_species) {
117    gb_assert(GB_has_key(gb_species, "species"));
118    return GB_next_marked(gb_species, "species");
119}
120
121GBDATA *GBT_first_species_rel_species_data(GBDATA *gb_species_data) {
122    return GB_entry(gb_species_data, "species");
123}
124GBDATA *GBT_first_species(GBDATA *gb_main) {
125    return GB_entry(GBT_get_species_data(gb_main), "species");
126}
127
128GBDATA *GBT_next_species(GBDATA *gb_species) {
129    gb_assert(GB_has_key(gb_species, "species"));
130    return GB_nextEntry(gb_species);
131}
132
133GBDATA *GBT_find_species_rel_species_data(GBDATA *gb_species_data, const char *name) {
134    return GBT_find_item_rel_item_data(gb_species_data, "name", name);
135}
136GBDATA *GBT_expect_species_rel_species_data(GBDATA *gb_species_data, const char *name) {
137    return GBT_expect_item_rel_item_data(gb_species_data, "name", name);
138}
139GBDATA *GBT_find_species(GBDATA *gb_main, const char *name) {
140    // Search for a species.
141    // Return found species or NULp (in this case an error MAY be exported).
142    //
143    // Note: If you expect the species to exists, use GBT_expect_species!
144    return GBT_find_item_rel_item_data(GBT_get_species_data(gb_main), "name", name);
145}
146GBDATA *GBT_expect_species(GBDATA *gb_main, const char *name) {
147    // Returns an existing species or
148    // NULp (in that case an error is exported)
149    return GBT_expect_item_rel_item_data(GBT_get_species_data(gb_main), "name", name);
150}
151
152// --------------------------------------------------------------------------------
153
154GBDATA *GBT_get_SAI_data(GBDATA *gb_main) {
155    return GBT_find_or_create(gb_main, "extended_data", 7);
156}
157
158// Search SAIs
159GBDATA *GBT_first_SAI_rel_SAI_data(GBDATA *gb_sai_data) {
160    return GB_entry(gb_sai_data, "extended");
161}
162GBDATA *GBT_first_SAI(GBDATA *gb_main) {
163    return GB_entry(GBT_get_SAI_data(gb_main), "extended");
164}
165
166GBDATA *GBT_next_SAI(GBDATA *gb_sai) {
167    gb_assert(GB_has_key(gb_sai, "extended"));
168    return GB_nextEntry(gb_sai);
169}
170
171GBDATA *GBT_find_SAI_rel_SAI_data(GBDATA *gb_sai_data, const char *name) {
172    return GBT_find_item_rel_item_data(gb_sai_data, "name", name);
173}
174GBDATA *GBT_expect_SAI_rel_SAI_data(GBDATA *gb_sai_data, const char *name) {
175    return GBT_expect_item_rel_item_data(gb_sai_data, "name", name);
176}
177GBDATA *GBT_find_SAI(GBDATA *gb_main, const char *name) {
178    // Search for a SAI.
179    // Return found SAI or NULp (in this case an error MAY be exported).
180    //
181    // Note: If you expect the SAI to exist, use GBT_expect_SAI!
182    return GBT_find_item_rel_item_data(GBT_get_SAI_data(gb_main), "name", name);
183}
184GBDATA *GBT_expect_SAI(GBDATA *gb_main, const char *name) {
185    // Returns an existing SAI or
186    // NULp (in that case an error is exported)
187    return GBT_expect_item_rel_item_data(GBT_get_SAI_data(gb_main), "name", name);
188}
189
190// ---------------------
191//      count items
192
193static long GBT_get_item_count(GBDATA *gb_parent_of_container, const char *item_container_name) {
194    // returns elements stored in a container
195
196    GBDATA *gb_item_data;
197    long    count = 0;
198
199    GB_push_transaction(gb_parent_of_container);
200    gb_item_data = GB_entry(gb_parent_of_container, item_container_name);
201    if (gb_item_data) count = GB_number_of_subentries(gb_item_data);
202    GB_pop_transaction(gb_parent_of_container);
203
204    return count;
205}
206
207long GBT_get_species_count(GBDATA *gb_main) {
208    return GBT_get_item_count(gb_main, "species_data");
209}
210
211long GBT_get_SAI_count(GBDATA *gb_main) {
212    return GBT_get_item_count(gb_main, "extended_data");
213}
214
215// --------------------------------------------------------------------------------
216
217static char *GBT_create_unique_item_identifier(GBDATA *gb_item_container, const char *id_field, const char *default_id) {
218    // returns an identifier not used by items in 'gb_item_container'
219    // 'id_field' is the entry that is used as identifier (e.g. 'name' for species)
220    // 'default_id' will be suffixed with a number to generate a unique id
221    //
222    // Note:
223    // * The resulting id may be longer than 8 characters
224    // * This function is slow, so just use in extra-ordinary situations
225
226    GBDATA *gb_item   = GBT_find_item_rel_item_data(gb_item_container, id_field, default_id);
227    char   *unique_id;
228
229    if (!gb_item) {
230        unique_id = ARB_strdup(default_id); // default_id is unused
231    }
232    else {
233        char   *generated_id  = ARB_alloc<char>(strlen(default_id)+20);
234        size_t  min_num = 1;
235
236#define GENERATE_ID(num) sprintf(generated_id, "%s%zu", default_id, num);
237
238        GENERATE_ID(min_num);
239        gb_item = GBT_find_item_rel_item_data(gb_item_container, id_field, generated_id);
240
241        if (gb_item) {
242            size_t num_items = GB_number_of_subentries(gb_item_container);
243            size_t max_num   = 0;
244
245            gb_assert(num_items); // otherwise deadlock below
246
247            do {
248                max_num += num_items;
249                GENERATE_ID(max_num);
250                gb_item  = GBT_find_item_rel_item_data(gb_item_container, id_field, generated_id);
251            } while (gb_item && max_num >= num_items);
252
253            if (max_num<num_items) { // overflow
254                char *uid;
255                generated_id[0] = 'a'+GB_random(26);
256                generated_id[1] = 'a'+GB_random(26);
257                generated_id[2] = 0;
258
259                uid = GBT_create_unique_item_identifier(gb_item_container, id_field, generated_id);
260                strcpy(generated_id, uid);
261                free(uid);
262            }
263            else {
264                // max_num is unused
265                while ((max_num-min_num)>1) {
266                    size_t mid = (min_num+max_num)/2;
267                    gb_assert(mid != min_num && mid != max_num);
268
269                    GENERATE_ID(mid);
270                    gb_item = GBT_find_item_rel_item_data(gb_item_container, id_field, generated_id);
271
272                    if (gb_item) min_num = mid;
273                    else max_num = mid;
274                }
275                GENERATE_ID(max_num);
276                gb_assert(!GBT_find_item_rel_item_data(gb_item_container, id_field, generated_id));
277            }
278        }
279        unique_id = generated_id;
280
281#undef GENERATE_ID
282    }
283
284    return unique_id;
285}
286
287char *GBT_create_unique_species_name(GBDATA *gb_main, const char *default_name) {
288    return GBT_create_unique_item_identifier(GBT_get_species_data(gb_main), "name", default_name);
289}
290
291
292// --------------------------------
293//      mark and unmark species
294
295void GBT_mark_all(GBDATA *gb_main, int flag) {
296    // flag == 0 -> unmark
297    // flag == 1 -> mark
298    // flag == 2 -> invert
299
300    GBDATA *gb_species;
301    GB_push_transaction(gb_main);
302
303    if (flag == 2) {
304        for (gb_species = GBT_first_species(gb_main);
305             gb_species;
306             gb_species = GBT_next_species(gb_species))
307        {
308            GB_write_flag(gb_species, !GB_read_flag(gb_species));
309        }
310    }
311    else {
312        gb_assert(flag == 0 || flag == 1);
313
314        for (gb_species = GBT_first_species(gb_main);
315             gb_species;
316             gb_species = GBT_next_species(gb_species))
317        {
318            GB_write_flag(gb_species, flag);
319        }
320    }
321    GB_pop_transaction(gb_main);
322}
323void GBT_mark_all_that(GBDATA *gb_main, int flag, bool (*condition)(GBDATA*, void*), void *cd) {
324    GBDATA *gb_species;
325    GB_push_transaction(gb_main);
326
327    if (flag == 2) {
328        for (gb_species = GBT_first_species(gb_main);
329             gb_species;
330             gb_species = GBT_next_species(gb_species))
331        {
332            if (condition(gb_species, cd)) {
333                GB_write_flag(gb_species, !GB_read_flag(gb_species));
334            }
335        }
336    }
337    else {
338        gb_assert(flag == 0 || flag == 1);
339
340        for (gb_species = GBT_first_species(gb_main);
341             gb_species;
342             gb_species = GBT_next_species(gb_species))
343        {
344            int curr_flag = GB_read_flag(gb_species);
345            if (curr_flag != flag && condition(gb_species, cd)) {
346                GB_write_flag(gb_species, flag);
347            }
348        }
349    }
350    GB_pop_transaction(gb_main);
351}
352
353long GBT_count_marked_species(GBDATA *gb_main) {
354    GB_transaction ta(gb_main);
355    return GB_number_of_marked_subentries(GBT_get_species_data(gb_main));
356}
357
358char *GBT_store_marked_species(GBDATA *gb_main, bool unmark_all) {
359    /*! stores the currently marked species in a string
360     * @param gb_main    database
361     * @param unmark_all if true -> unmark species
362     * @return ';'-separated list of species names
363     *
364     * Note: a faster (but less robust) way to temporarily store species marks,
365     *       is to use the flag GB_USERFLAG_WASMARKED together with GB_write_user_flag
366     */
367    GBS_strstruct *out = GBS_stropen(10000);
368    GBDATA        *gb_species;
369
370    for (gb_species = GBT_first_marked_species(gb_main);
371         gb_species;
372         gb_species = GBT_next_marked_species(gb_species))
373    {
374        const char *name = GBT_get_name(gb_species);
375        if (name) {
376            GBS_strcat(out, name);
377            GBS_chrcat(out, ';');
378            if (unmark_all) GB_write_flag(gb_species, 0);
379        }
380    }
381
382    GBS_str_cut_tail(out, 1); // remove trailing ';'
383    return GBS_strclose(out);
384}
385
386NOT4PERL GB_ERROR GBT_with_stored_species(GBDATA *gb_main, const char *stored, species_callback doit, int *clientdata) {
387    /*! call a function with each species of a list
388     * @param gb_main    database
389     * @param stored     ';'-separated list of species names
390     * @param doit       function to call with each species in 'stored'
391     * @param clientdata is passed to 'doit'
392     * @return error if sth goes wrong (or if 'doit' reports error)
393     */
394
395#define MAX_NAME_LEN 20
396    char     name[MAX_NAME_LEN+1];
397    GB_ERROR error = NULp;
398
399    while (!error) {
400        const char   *p   = strchr(stored, ';');
401        int     len = p ? (p-stored) : (int)strlen(stored);
402        GBDATA *gb_species;
403
404        gb_assert(len <= MAX_NAME_LEN);
405        memcpy(name, stored, len);
406        name[len] = 0;
407
408        gb_species = GBT_find_species(gb_main, name);
409        if (gb_species) {
410            error = doit(gb_species, clientdata);
411        }
412        else {
413            error = "Some stored species where not found.";
414        }
415
416        if (!p) break;
417        stored = p+1;
418    }
419#undef MAX_NAME_LEN
420    return error;
421}
422
423static GB_ERROR restore_mark(GBDATA *gb_species, int *) {
424    GB_write_flag(gb_species, 1);
425    return NULp;
426}
427
428GB_ERROR GBT_restore_marked_species(GBDATA *gb_main, const char *stored_marked) {
429    /*! restores marked species.
430     * @param gb_main       database
431     * @param stored_marked contains a ';'-separated list of species names to mark (as returned by GBT_store_marked_species)
432     * @return error if sth goes wrong
433     */
434    GBT_mark_all(gb_main, 0);   // unmark all species
435    return GBT_with_stored_species(gb_main, stored_marked, restore_mark, NULp);
436}
437
438// ---------------------------------
439//      read species information
440
441GB_CSTR GBT_get_name_or_description(GBDATA *gb_item) {
442    /*! returns the value of the "name" field of any database item or "<unnamed_item>" or similar.
443     *  @see GBT_get_name
444     */
445    GB_CSTR result      = GBT_read_char_pntr(gb_item, "name");
446    if (!result) result = GBS_global_string("<unnamed_%s>", GB_read_key_pntr(gb_item));
447    return result;
448}
449
450const char *GBT_get_name(GBDATA *gb_item) {
451    /*! returns the value of the "name" field of any database item or NULp.
452     *  @see GBT_get_name_or_description
453     */
454    GBDATA *gb_name = GB_entry(gb_item, "name");
455    if (!gb_name) return NULp;
456    return GB_read_char_pntr(gb_name);
457}
458
459GB_CSTR GBT_read_name(GBDATA *gb_item) {
460    // goes to header: __ATTR__DEPRECATED_TODO("please use GBT_get_name or GBT_get_name_or_description()")
461
462    // @@@ r18159 unintentionally broke SINA (binary) compatibility
463    // (remove this function when no longer needed)
464    return GBT_get_name_or_description(gb_item);
465}
466
467GBDATA **GBT_gen_species_array(GBDATA *gb_main, long *speciesCountPtr) {
468    GBDATA *gb_species_data = GBT_get_species_data(gb_main);
469
470    GBDATA **result       = NULp;
471    long     speciesCount = 0;
472
473    for (GBDATA *gb_species = GBT_first_species_rel_species_data(gb_species_data);
474         gb_species;
475         gb_species = GBT_next_species(gb_species))
476    {
477        speciesCount ++;
478    }
479
480    if (speciesCount) {
481        ARB_alloc(result, speciesCount);
482
483        speciesCount    = 0;
484        for (GBDATA *gb_species = GBT_first_species_rel_species_data(gb_species_data);
485             gb_species;
486             gb_species = GBT_next_species(gb_species))
487        {
488            result[speciesCount++] = gb_species;
489        }
490    }
491
492    *speciesCountPtr = speciesCount;
493    return result;
494}
495
Note: See TracBrowser for help on using the repository browser.