source: branches/stable/ARBDB/adname.cxx

Last change on this file was 18665, checked in by westram, 3 years ago
  • change many WARN_TODO triggered warnings into todo markers.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.8 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 "TreeNode.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#include "ad_colorset.h"
24
25struct gbt_renamed {
26    int     used_by;
27    char    data[1];
28};
29
30static struct {
31    GB_HASH *renamed_hash;
32    GB_HASH *old_species_hash;
33    GBDATA  *gb_main;
34    GBDATA  *gb_species_data;
35    int      all_flag;
36} NameSession;
37
38GB_ERROR GBT_begin_rename_session(GBDATA *gb_main, int all_flag) {
39    /* Starts a rename session (to rename one or many species)
40     * all_flag == 1 -> rename all species in DB
41     * Call GBT_abort_rename_session() or GBT_commit_rename_session() to close the session.
42     */
43
44    // @@@ change all_flag into estimated number of renames ( == 0 shall mean all)
45
46    GB_ERROR error = GB_push_transaction(gb_main);
47    if (!error) {
48        NameSession.gb_main         = gb_main;
49        NameSession.gb_species_data = GBT_get_species_data(gb_main);
50
51        if (!all_flag) { // this is meant to be used for single or few species
52            int hash_size = 128;
53
54            NameSession.renamed_hash     = GBS_create_dynaval_hash(hash_size, GB_MIND_CASE, GBS_dynaval_free);
55            NameSession.old_species_hash = NULp;
56        }
57        else {
58            NameSession.renamed_hash     = GBS_create_dynaval_hash(GBT_get_species_count(gb_main), GB_MIND_CASE, GBS_dynaval_free);
59            NameSession.old_species_hash = GBT_create_species_hash(gb_main);
60        }
61        NameSession.all_flag = all_flag;
62    }
63    return error;
64}
65
66GB_ERROR GBT_rename_species(const char *oldname, const  char *newname, bool ignore_protection) {
67    GBDATA   *gb_species;
68    GBDATA   *gb_name;
69    GB_ERROR  error;
70
71    if (strcmp(oldname, newname) == 0) return NULp;
72
73#if defined(DEBUG) && 1
74    if (isdigit(oldname[0])) {
75        printf("oldname='%s' newname='%s'\n", oldname, newname);
76    }
77#endif
78
79    if (NameSession.all_flag) {
80        gb_assert(NameSession.old_species_hash);
81        gb_species = (GBDATA *)GBS_read_hash(NameSession.old_species_hash, oldname);
82    }
83    else {
84        GBDATA *gb_found_species;
85
86        gb_assert(!NameSession.old_species_hash);
87        gb_found_species = GBT_find_species_rel_species_data(NameSession.gb_species_data, newname);
88        gb_species       = GBT_find_species_rel_species_data(NameSession.gb_species_data, oldname);
89
90        if (gb_found_species && gb_species != gb_found_species) {
91            return GBS_global_string("A species named '%s' already exists.", newname);
92        }
93    }
94
95    if (!gb_species) {
96        return GBS_global_string("Expected that a species named '%s' exists (maybe there are duplicate species, database might be corrupt)", oldname);
97    }
98
99    gb_name = GB_entry(gb_species, "name");
100    {
101        SmartPtr<GB_topSecurityLevel> probablyUnsecured;
102        if (ignore_protection) probablyUnsecured = new GB_topSecurityLevel(NameSession.gb_main);
103        error = GB_write_string(gb_name, newname);
104    }
105
106    if (!error) {
107        if (NameSession.old_species_hash) {
108            GBS_write_hash(NameSession.old_species_hash, oldname, 0);
109        }
110        gbt_renamed *rns = (gbt_renamed*)ARB_calloc<char>(strlen(newname)+sizeof(gbt_renamed));
111        strcpy(&rns->data[0], newname);
112        GBS_write_hash(NameSession.renamed_hash, oldname, (long)rns);
113    }
114    return error;
115}
116
117static void gbt_free_rename_session_data() {
118    if (NameSession.renamed_hash) {
119        GBS_free_hash(NameSession.renamed_hash);
120        NameSession.renamed_hash = NULp;
121    }
122    if (NameSession.old_species_hash) {
123        GBS_free_hash(NameSession.old_species_hash);
124        NameSession.old_species_hash = NULp;
125    }
126}
127
128GB_ERROR GBT_abort_rename_session() {
129    gbt_free_rename_session_data();
130    return GB_abort_transaction(NameSession.gb_main);
131}
132
133static const char *currentTreeName = NULp;
134
135static GB_ERROR gbt_rename_tree_rek(TreeNode *tree, int tree_index) {
136    if (tree) {
137        if (tree->is_leaf()) {
138            if (tree->name) {
139                gbt_renamed *rns = (gbt_renamed *)GBS_read_hash(NameSession.renamed_hash, tree->name);
140                if (rns) {
141                    char *newname;
142                    if (rns->used_by == tree_index) { // species more than once in the tree
143                        static int counter = 0;
144                        char       buffer[256];
145
146                        sprintf(buffer, "%s_%i", rns->data, counter++);
147                        GB_warningf("Species '%s' more than once in '%s', creating zombie '%s'",
148                                    tree->name, currentTreeName, buffer);
149                        newname = buffer;
150                    }
151                    else {
152                        newname = &rns->data[0];
153                    }
154                    freedup(tree->name, newname);
155                    rns->used_by = tree_index;
156                }
157            }
158        }
159        else {
160            gbt_rename_tree_rek(tree->get_leftson(), tree_index);
161            gbt_rename_tree_rek(tree->get_rightson(), tree_index);
162        }
163    }
164    return NULp;
165}
166
167GB_ERROR GBT_commit_rename_session() { // goes to header: __ATTR__USERESULT
168    bool         is_genome_db = GEN_is_genome_db(NameSession.gb_main, -1);
169    arb_progress commit_progress("Correcting name references", long(3+is_genome_db));
170    GB_ERROR     error        = NULp;
171
172    commit_progress.allow_title_reuse();
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            size_t       tree_count = tree_names.size();
181            arb_progress progress(GBS_global_string("Correcting names in %zu tree%c", tree_count, "s"[tree_count<2]), tree_count*3);
182
183            for (size_t count = 0; count<tree_count && !error; ++count) {
184                const char *tname = tree_names[count];
185                TreeNode   *tree  = GBT_read_tree(NameSession.gb_main, tname, new SimpleRoot);
186                ++progress;
187
188                if (tree) {
189                    currentTreeName = tname; // provide tree name (used for error message)
190                    gbt_rename_tree_rek(tree, count+1);
191                    currentTreeName = NULp;
192
193                    ++progress;
194
195                    GBT_write_tree(NameSession.gb_main, tname, tree);
196                    destroy(tree);
197
198                    progress.inc_and_check_user_abort(error);
199                }
200                else {
201                    GBT_message(NameSession.gb_main, GBS_global_string("Warning: failed to read '%s'\n"
202                                                                       "(Reason: %s)\n"
203                                                                       "Please note that this tree is useless now!",
204                                                                       tname, GB_await_error()));
205                    ++progress;
206                    ++progress;
207                }
208            }
209        }
210        commit_progress.inc_and_check_user_abort(error);
211    }
212    // rename configurations
213    if (!error) {
214        ConstStrArray config_names;
215        GBT_get_configuration_names(config_names, NameSession.gb_main);
216
217        if (!config_names.empty()) {
218            size_t       config_count = config_names.size();
219            arb_progress progress(GBS_global_string("Correcting names in %zu config%c", config_count, "s"[config_count<2]), config_count);
220
221            for (size_t count = 0; !error && count<config_count; ++count) {
222                GBT_config config(NameSession.gb_main, config_names[count], error);
223                if (!error) {
224                    int need_save = 0;
225                    for (int area = GBT_config::TOP_AREA; !error && area<=GBT_config::MIDDLE_AREA; ++area) {
226                        GBT_config_parser  parser(config, area);
227                        GBS_strstruct     *strstruct = GBS_stropen(1000);
228
229                        while (1) {
230                            const GBT_config_item& item = parser.nextItem(error);
231                            if (error || item.type == CI_END_OF_CONFIG) break;
232
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                                    GBT_append_to_config_string(GBT_config_item(CI_SPECIES, rns->data), strstruct);
237                                    need_save = 1;
238                                    continue;
239                                }
240                            }
241                            GBT_append_to_config_string(item, strstruct);
242                        }
243
244                        if (!error) {
245                            config.set_definition(area, GBS_strclose(strstruct));
246                        }
247                        else {
248                            error = GBS_global_string("Failed to parse configuration '%s' (Reason: %s)", config_names[count], error);
249                            GBS_strforget(strstruct);
250                        }
251                    }
252
253                    if (!error && need_save) {
254                        error = config.save(NameSession.gb_main, config_names[count], false);
255                    }
256                }
257                progress.inc_and_check_user_abort(error);
258            }
259        }
260        commit_progress.inc_and_check_user_abort(error);
261    }
262
263    // rename species in saved colorsets
264    if (!error) {
265        GBDATA *gb_species_colorset_root = GBT_colorset_root(NameSession.gb_main, "species");
266        if (gb_species_colorset_root) {
267            ConstStrArray colorset_names;
268            GBT_get_colorset_names(colorset_names, gb_species_colorset_root);
269
270            size_t colorset_count = colorset_names.size();
271            if (colorset_count>0) {
272                arb_progress progress(GBS_global_string("Correcting names in %zu colorset%c", colorset_count, "s"[colorset_count<2]), colorset_count);
273
274                for (size_t c = 0; c<colorset_count && !error; ++c) {
275                    GBDATA *gb_colorset     = GBT_find_colorset(gb_species_colorset_root, colorset_names[c]);
276                    if (!gb_colorset) error = GB_await_error();
277                    else {
278                        ConstStrArray colorDefs;
279                        error = GBT_load_colorset(gb_colorset, colorDefs);
280                        if (!error) {
281                            StrArray modifiedDefs;
282                            bool     changed = false;
283
284                            for (int d = colorDefs.size()-1; d>=0; --d) {
285                                const char *def   = colorDefs[d];
286                                const char *equal = strchr(def, '=');
287
288                                if (equal) { // only handle correct entries (do not touch rest)
289                                    if (strcmp(equal+1, "0") == 0) { // unneeded "no color"-entry (see [14094])
290                                        colorDefs.remove(d);
291                                        changed = true;
292                                    }
293                                    else {
294                                        gbt_renamed *rns;
295                                        {
296                                            LocallyModify<char>  tempSplit(const_cast<char*>(equal)[0], 0);
297                                            rns = (gbt_renamed *)GBS_read_hash(NameSession.renamed_hash, def);
298                                        }
299                                        if (rns) { // species was renamed
300                                            char *newDef = GBS_global_string_copy("%s%s", rns->data, equal);
301                                            colorDefs.replace(d, newDef); // replace colorDefs
302                                            modifiedDefs.put(newDef);     // keep heapcopy until colorDefs gets written
303
304                                            changed = true;
305                                        }
306                                    }
307                                }
308                            }
309
310                            if (changed && !error) error = GBT_save_colorset(gb_colorset, colorDefs);
311                        }
312                    }
313                    progress.inc_and_check_user_abort(error);
314                }
315            }
316        }
317        commit_progress.inc_and_check_user_abort(error);
318    }
319
320    // rename links in pseudo-species
321    if (!error && is_genome_db) {
322        {
323            arb_progress progress("Correcting names of organism references");
324
325            GBDATA *gb_pseudo;
326            for (gb_pseudo = GEN_first_pseudo_species(NameSession.gb_main);
327                 gb_pseudo && !error;
328                 gb_pseudo = GEN_next_pseudo_species(gb_pseudo))
329            {
330                GBDATA *gb_origin_organism = GB_entry(gb_pseudo, "ARB_origin_species");
331                if (gb_origin_organism) {
332                    const char  *origin = GB_read_char_pntr(gb_origin_organism);
333                    gbt_renamed *rns    = (gbt_renamed *)GBS_read_hash(NameSession.renamed_hash, origin);
334                    if (rns) {          // species was renamed
335                        const char *newname = &rns->data[0];
336                        error               = GB_write_string(gb_origin_organism, newname);
337                    }
338                }
339            }
340        }
341        commit_progress.inc_and_check_user_abort(error);
342    }
343
344    gbt_free_rename_session_data();
345
346    error = GB_end_transaction(NameSession.gb_main, error);
347    return error;
348}
349
350// --------------------------------------------------------------------------------
351
352#ifdef UNIT_TESTS
353#ifndef TEST_UNIT_H
354#include <test_unit.h>
355#endif
356
357// #define TEST_AUTO_UPDATE // uncomment to auto-update test result db
358
359void TEST_SLOW_rename_session() {
360    const char *inputname    = "TEST_opti_ascii_in.arb";
361    const char *outputname   = "TEST_opti_ascii_renamed.arb";
362    const char *expectedname = "TEST_opti_ascii_renamed_expected.arb";
363
364    {
365        GB_shell  shell;
366        GBDATA   *gb_main;
367        TEST_EXPECT_RESULT__NOERROREXPORTED(gb_main = GB_open(inputname, "rw"));
368
369        for (int session = 1; session <= 2; ++session) {
370            TEST_ANNOTATE(GBS_global_string("session=%i", session));
371
372            TEST_EXPECT_NO_ERROR(GBT_begin_rename_session(gb_main, 0));
373            if (session == 2) { // session 1 tests renaming nothing
374                // only in config 'some':
375                TEST_EXPECT_NO_ERROR(GBT_rename_species("FrnPhilo", "olihPnrF", true));
376                TEST_EXPECT_NO_ERROR(GBT_rename_species("DsfDesul", "luseDfsD", true));
377                // also in config 'other':
378                TEST_EXPECT_NO_ERROR(GBT_rename_species("CalSacch", "hccaSlaC", true));
379                TEST_EXPECT_NO_ERROR(GBT_rename_species("LacReute", "etueRcaL", true));
380            }
381            TEST_EXPECT_NO_ERROR(GBT_commit_rename_session());
382        }
383
384        TEST_EXPECT_NO_ERROR(GB_save_as(gb_main, outputname, "a"));
385        GB_close(gb_main);
386    }
387
388#if defined(TEST_AUTO_UPDATE)
389    TEST_COPY_FILE(outputname, expectedname);
390#endif
391    TEST_EXPECT_TEXTFILE_DIFFLINES(outputname, expectedname, 0);
392    TEST_EXPECT_ZERO_OR_SHOW_ERRNO(GB_unlink(outputname));
393}
394TEST_PUBLISH(TEST_SLOW_rename_session);
395
396#endif // UNIT_TESTS
397
398// --------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.