source: branches/profile/ARBDB/adname.cxx

Last change on this file was 12438, checked in by westram, 10 years ago
  • activate errors on missing test-source location
    • publish all missing tests
    • missing location apparently only occurs
      • in dynamic libraries and
      • for last test of a module Note: the reverse is not true!
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.6 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : adname.cxx                                        //
4//   Purpose   : species names                                     //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "gb_local.h"
12
13#include <ad_config.h>
14#include <arbdbt.h>
15
16#include <arb_progress.h>
17#include <arb_strbuf.h>
18#include <arb_strarray.h>
19#include <arb_file.h>
20#include <arb_diff.h>
21
22#include <cctype>
23
24struct gbt_renamed {
25    int     used_by;
26    char    data[1];
27};
28
29static struct {
30    GB_HASH *renamed_hash;
31    GB_HASH *old_species_hash;
32    GBDATA  *gb_main;
33    GBDATA  *gb_species_data;
34    int      all_flag;
35} NameSession;
36
37#if defined(WARN_TODO)
38#warning change all_flag into estimated number of renames ( == 0 shall mean all)
39#endif
40
41GB_ERROR GBT_begin_rename_session(GBDATA *gb_main, int all_flag) {
42    /* Starts a rename session (to rename one or many species)
43     * all_flag == 1 -> rename all species in DB
44     * Call GBT_abort_rename_session() or GBT_commit_rename_session() to close the session.
45     */
46
47    GB_ERROR error = GB_push_transaction(gb_main);
48    if (!error) {
49        NameSession.gb_main         = gb_main;
50        NameSession.gb_species_data = GBT_get_species_data(gb_main);
51
52        if (!all_flag) { // this is meant to be used for single or few species
53            int hash_size = 128;
54
55            NameSession.renamed_hash     = GBS_create_dynaval_hash(hash_size, GB_MIND_CASE, GBS_dynaval_free);
56            NameSession.old_species_hash = 0;
57        }
58        else {
59            NameSession.renamed_hash     = GBS_create_dynaval_hash(GBT_get_species_count(gb_main), GB_MIND_CASE, GBS_dynaval_free);
60            NameSession.old_species_hash = GBT_create_species_hash(gb_main);
61        }
62        NameSession.all_flag = all_flag;
63    }
64    return error;
65}
66
67GB_ERROR GBT_rename_species(const char *oldname, const  char *newname, bool ignore_protection)
68{
69    GBDATA   *gb_species;
70    GBDATA   *gb_name;
71    GB_ERROR  error;
72
73    if (strcmp(oldname, newname) == 0)
74        return 0;
75
76#if defined(DEBUG) && 1
77    if (isdigit(oldname[0])) {
78        printf("oldname='%s' newname='%s'\n", oldname, newname);
79    }
80#endif
81
82    if (NameSession.all_flag) {
83        gb_assert(NameSession.old_species_hash);
84        gb_species = (GBDATA *)GBS_read_hash(NameSession.old_species_hash, oldname);
85    }
86    else {
87        GBDATA *gb_found_species;
88
89        gb_assert(NameSession.old_species_hash == 0);
90        gb_found_species = GBT_find_species_rel_species_data(NameSession.gb_species_data, newname);
91        gb_species       = GBT_find_species_rel_species_data(NameSession.gb_species_data, oldname);
92
93        if (gb_found_species && gb_species != gb_found_species) {
94            return GB_export_errorf("A species named '%s' already exists.", newname);
95        }
96    }
97
98    if (!gb_species) {
99        return GB_export_errorf("Expected that a species named '%s' exists (maybe there are duplicate species, database might be corrupt)", oldname);
100    }
101
102    gb_name = GB_entry(gb_species, "name");
103    if (ignore_protection) GB_push_my_security(NameSession.gb_main);
104    error   = GB_write_string(gb_name, newname);
105    if (ignore_protection) GB_pop_my_security(NameSession.gb_main);
106
107    if (!error) {
108        if (NameSession.old_species_hash) {
109            GBS_write_hash(NameSession.old_species_hash, oldname, 0);
110        }
111        gbt_renamed *rns = (gbt_renamed *)GB_calloc(strlen(newname) + sizeof (gbt_renamed), sizeof(char));
112        strcpy(&rns->data[0], newname);
113        GBS_write_hash(NameSession.renamed_hash, oldname, (long)rns);
114    }
115    return error;
116}
117
118static void gbt_free_rename_session_data() {
119    if (NameSession.renamed_hash) {
120        GBS_free_hash(NameSession.renamed_hash);
121        NameSession.renamed_hash = 0;
122    }
123    if (NameSession.old_species_hash) {
124        GBS_free_hash(NameSession.old_species_hash);
125        NameSession.old_species_hash = 0;
126    }
127}
128
129GB_ERROR GBT_abort_rename_session() {
130    gbt_free_rename_session_data();
131    return GB_abort_transaction(NameSession.gb_main);
132}
133
134static const char *currentTreeName = 0;
135
136static GB_ERROR gbt_rename_tree_rek(GBT_TREE *tree, int tree_index) {
137    if (tree) {
138        if (tree->is_leaf) {
139            if (tree->name) {
140                gbt_renamed *rns = (gbt_renamed *)GBS_read_hash(NameSession.renamed_hash, tree->name);
141                if (rns) {
142                    char *newname;
143                    if (rns->used_by == tree_index) { // species more than once in the tree
144                        static int counter = 0;
145                        char       buffer[256];
146
147                        sprintf(buffer, "%s_%i", rns->data, counter++);
148                        GB_warningf("Species '%s' more than once in '%s', creating zombie '%s'",
149                                    tree->name, currentTreeName, buffer);
150                        newname = buffer;
151                    }
152                    else {
153                        newname = &rns->data[0];
154                    }
155                    freedup(tree->name, newname);
156                    rns->used_by = tree_index;
157                }
158            }
159        }
160        else {
161            gbt_rename_tree_rek(tree->leftson, tree_index);
162            gbt_rename_tree_rek(tree->rightson, tree_index);
163        }
164    }
165    return NULL;
166}
167
168GB_ERROR GBT_commit_rename_session() { // goes to header: __ATTR__USERESULT
169    arb_progress commit_progress("Renaming name references", 3);
170    commit_progress.allow_title_reuse();
171
172    GB_ERROR error = 0;
173
174    // rename species in trees
175    {
176        ConstStrArray tree_names;
177        GBT_get_tree_names(tree_names, NameSession.gb_main, false);
178
179        if (!tree_names.empty()) {
180            int          tree_count = tree_names.size();
181            arb_progress progress(GBS_global_string("Renaming species in %i tree%c", tree_count, "s"[tree_count<2]),
182                                  tree_count*3);
183
184            for (int count = 0; count<tree_count && !error; ++count) {
185                const char *tname = tree_names[count];
186                GBT_TREE   *tree  = GBT_read_tree(NameSession.gb_main, tname, GBT_TREE_NodeFactory());
187                ++progress;
188
189                if (tree) {
190                    currentTreeName = tname; // provide tree name (used for error message)
191                    gbt_rename_tree_rek(tree, count+1);
192                    currentTreeName = 0;
193
194                    ++progress;
195
196                    GBT_write_tree(NameSession.gb_main, tname, tree);
197                    delete tree;
198
199                    progress.inc_and_check_user_abort(error);
200                }
201                else {
202                    GBT_message(NameSession.gb_main, GBS_global_string("Warning: failed to read '%s' (Reason: %s)", tname, GB_await_error()));
203                    ++progress;
204                    ++progress;
205                }
206            }
207        }
208        commit_progress.inc_and_check_user_abort(error);
209    }
210    // rename configurations
211    if (!error) {
212        ConstStrArray config_names;
213        GBT_get_configuration_names(config_names, NameSession.gb_main);
214
215        if (!config_names.empty()) {
216            int          config_count = config_names.size();
217            arb_progress progress(GBS_global_string("Renaming species in %i config%c", config_count, "s"[config_count<2]), config_count);
218
219            for (int count = 0; !error && count<config_count; ++count) {
220                GBT_config *config = GBT_load_configuration_data(NameSession.gb_main, config_names[count], &error);
221                if (!error) {
222                    int need_save = 0;
223                    int mode;
224
225                    for (mode = 0; !error && mode<2; ++mode) {
226                        char              **configStrPtr = (mode == 0 ? &config->top_area : &config->middle_area);
227                        GBT_config_parser  *parser       = GBT_start_config_parser(*configStrPtr);
228                        GBT_config_item    *item         = GBT_create_config_item();
229                        GBS_strstruct      *strstruct    = GBS_stropen(1000);
230
231                        error = GBT_parse_next_config_item(parser, item);
232                        while (!error && item->type != CI_END_OF_CONFIG) {
233                            if (item->type == CI_SPECIES) {
234                                gbt_renamed *rns = (gbt_renamed *)GBS_read_hash(NameSession.renamed_hash, item->name);
235                                if (rns) { // species was renamed
236                                    freedup(item->name, rns->data);
237                                    need_save = 1;
238                                }
239                            }
240                            GBT_append_to_config_string(item, strstruct);
241                            error = GBT_parse_next_config_item(parser, item);
242                        }
243
244                        if (!error) freeset(*configStrPtr, GBS_strclose(strstruct));
245                        else {
246                            error = GBS_global_string("Failed to parse configuration '%s' (Reason: %s)", config_names[count], error);
247                        }
248
249                        GBT_free_config_item(item);
250                        GBT_free_config_parser(parser);
251                    }
252
253                    if (!error && need_save) {
254                        error = GBT_save_configuration_data(config, NameSession.gb_main, config_names[count]);
255                    }
256                    GBT_free_configuration_data(config);
257                }
258                progress.inc_and_check_user_abort(error);
259            }
260        }
261    }
262    commit_progress.inc_and_check_user_abort(error);
263
264    // rename links in pseudo-species
265    if (!error && GEN_is_genome_db(NameSession.gb_main, -1)) {
266        GBDATA *gb_pseudo;
267        for (gb_pseudo = GEN_first_pseudo_species(NameSession.gb_main);
268             gb_pseudo && !error;
269             gb_pseudo = GEN_next_pseudo_species(gb_pseudo))
270        {
271            GBDATA *gb_origin_organism = GB_entry(gb_pseudo, "ARB_origin_species");
272            if (gb_origin_organism) {
273                const char  *origin = GB_read_char_pntr(gb_origin_organism);
274                gbt_renamed *rns    = (gbt_renamed *)GBS_read_hash(NameSession.renamed_hash, origin);
275                if (rns) {          // species was renamed
276                    const char *newname = &rns->data[0];
277                    error               = GB_write_string(gb_origin_organism, newname);
278                }
279            }
280        }
281    }
282    commit_progress.inc_and_check_user_abort(error);
283
284    gbt_free_rename_session_data();
285
286    error = GB_end_transaction(NameSession.gb_main, error);
287    return error;
288}
289
290// --------------------------------------------------------------------------------
291
292#ifdef UNIT_TESTS
293#ifndef TEST_UNIT_H
294#include <test_unit.h>
295#endif
296
297// #define TEST_AUTO_UPDATE // uncomment to auto-update test result db
298
299void TEST_SLOW_rename_session() {
300    const char *inputname    = "TEST_opti_ascii_in.arb";
301    const char *outputname   = "TEST_opti_ascii_renamed.arb";
302    const char *expectedname = "TEST_opti_ascii_renamed_expected.arb";
303
304    {
305        GB_shell  shell;
306        GBDATA   *gb_main;
307        TEST_EXPECT_RESULT__NOERROREXPORTED(gb_main = GB_open(inputname, "rw"));
308
309        for (int session = 1; session <= 2; ++session) {
310            TEST_ANNOTATE(GBS_global_string("session=%i", session));
311
312            TEST_EXPECT_NO_ERROR(GBT_begin_rename_session(gb_main, 0));
313            if (session == 2) { // session 1 tests renaming nothing
314                // only in config 'some':
315                TEST_EXPECT_NO_ERROR(GBT_rename_species("FrnPhilo", "olihPnrF", true));
316                TEST_EXPECT_NO_ERROR(GBT_rename_species("DsfDesul", "luseDfsD", true));
317                // also in config 'other':
318                TEST_EXPECT_NO_ERROR(GBT_rename_species("CalSacch", "hccaSlaC", true));
319                TEST_EXPECT_NO_ERROR(GBT_rename_species("LacReute", "etueRcaL", true));
320            }
321            TEST_EXPECT_NO_ERROR(GBT_commit_rename_session());
322        }
323
324        TEST_EXPECT_NO_ERROR(GB_save_as(gb_main, outputname, "a"));
325        GB_close(gb_main);
326    }
327
328#if defined(TEST_AUTO_UPDATE)
329    TEST_COPY_FILE(outputname, expectedname);
330#endif
331    TEST_EXPECT_TEXTFILE_DIFFLINES(outputname, expectedname, 0);
332    TEST_EXPECT_ZERO_OR_SHOW_ERRNO(GB_unlink(outputname));
333}
334TEST_PUBLISH(TEST_SLOW_rename_session);
335
336#endif // UNIT_TESTS
337
338// --------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.