source: tags/ms_r18q1/ARBDB/adname.cxx

Last change on this file was 17110, checked in by westram, 6 years ago
  • 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
38#if defined(WARN_TODO)
39#warning change all_flag into estimated number of renames ( == 0 shall mean all)
40#endif
41
42GB_ERROR GBT_begin_rename_session(GBDATA *gb_main, int all_flag) {
43    /* Starts a rename session (to rename one or many species)
44     * all_flag == 1 -> rename all species in DB
45     * Call GBT_abort_rename_session() or GBT_commit_rename_session() to close the session.
46     */
47
48    GB_ERROR error = GB_push_transaction(gb_main);
49    if (!error) {
50        NameSession.gb_main         = gb_main;
51        NameSession.gb_species_data = GBT_get_species_data(gb_main);
52
53        if (!all_flag) { // this is meant to be used for single or few species
54            int hash_size = 128;
55
56            NameSession.renamed_hash     = GBS_create_dynaval_hash(hash_size, GB_MIND_CASE, GBS_dynaval_free);
57            NameSession.old_species_hash = NULp;
58        }
59        else {
60            NameSession.renamed_hash     = GBS_create_dynaval_hash(GBT_get_species_count(gb_main), GB_MIND_CASE, GBS_dynaval_free);
61            NameSession.old_species_hash = GBT_create_species_hash(gb_main);
62        }
63        NameSession.all_flag = all_flag;
64    }
65    return error;
66}
67
68GB_ERROR GBT_rename_species(const char *oldname, const  char *newname, bool ignore_protection) {
69    GBDATA   *gb_species;
70    GBDATA   *gb_name;
71    GB_ERROR  error;
72
73    if (strcmp(oldname, newname) == 0) return NULp;
74
75#if defined(DEBUG) && 1
76    if (isdigit(oldname[0])) {
77        printf("oldname='%s' newname='%s'\n", oldname, newname);
78    }
79#endif
80
81    if (NameSession.all_flag) {
82        gb_assert(NameSession.old_species_hash);
83        gb_species = (GBDATA *)GBS_read_hash(NameSession.old_species_hash, oldname);
84    }
85    else {
86        GBDATA *gb_found_species;
87
88        gb_assert(!NameSession.old_species_hash);
89        gb_found_species = GBT_find_species_rel_species_data(NameSession.gb_species_data, newname);
90        gb_species       = GBT_find_species_rel_species_data(NameSession.gb_species_data, oldname);
91
92        if (gb_found_species && gb_species != gb_found_species) {
93            return GBS_global_string("A species named '%s' already exists.", newname);
94        }
95    }
96
97    if (!gb_species) {
98        return GBS_global_string("Expected that a species named '%s' exists (maybe there are duplicate species, database might be corrupt)", oldname);
99    }
100
101    gb_name = GB_entry(gb_species, "name");
102    if (ignore_protection) GB_push_my_security(NameSession.gb_main);
103    error   = GB_write_string(gb_name, newname);
104    if (ignore_protection) GB_pop_my_security(NameSession.gb_main);
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", 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            int          tree_count = tree_names.size();
181            arb_progress progress(GBS_global_string("Correcting names 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                TreeNode   *tree  = GBT_read_tree(NameSession.gb_main, tname, new SimpleRoot);
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 = NULp;
193
194                    ++progress;
195
196                    GBT_write_tree(NameSession.gb_main, tname, tree);
197                    destroy(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'\n"
203                                                                       "(Reason: %s)\n"
204                                                                       "Please note that this tree is useless now!",
205                                                                       tname, GB_await_error()));
206                    ++progress;
207                    ++progress;
208                }
209            }
210        }
211        commit_progress.inc_and_check_user_abort(error);
212    }
213    // rename configurations
214    if (!error) {
215        ConstStrArray config_names;
216        GBT_get_configuration_names(config_names, NameSession.gb_main);
217
218        if (!config_names.empty()) {
219            int          config_count = config_names.size();
220            arb_progress progress(GBS_global_string("Correcting names in %i config%c", config_count, "s"[config_count<2]), config_count);
221
222            for (int count = 0; !error && count<config_count; ++count) {
223                GBT_config config(NameSession.gb_main, config_names[count], error);
224                if (!error) {
225                    int need_save = 0;
226                    for (int area = 0; !error && area<2; ++area) {
227                        GBT_config_parser   parser(config, area);
228                        GBS_strstruct      *strstruct    = GBS_stropen(1000);
229
230                        while (1) {
231                            const GBT_config_item& item = parser.nextItem(error);
232                            if (error || item.type == CI_END_OF_CONFIG) break;
233
234                            if (item.type == CI_SPECIES) {
235                                gbt_renamed *rns = (gbt_renamed *)GBS_read_hash(NameSession.renamed_hash, item.name);
236                                if (rns) { // species was renamed
237                                    GBT_append_to_config_string(GBT_config_item(CI_SPECIES, rns->data), strstruct);
238                                    need_save = 1;
239                                    continue;
240                                }
241                            }
242                            GBT_append_to_config_string(item, strstruct);
243                        }
244
245                        if (!error) {
246                            config.set_definition(area, GBS_strclose(strstruct));
247                        }
248                        else {
249                            error = GBS_global_string("Failed to parse configuration '%s' (Reason: %s)", config_names[count], error);
250                            GBS_strforget(strstruct);
251                        }
252                    }
253
254                    if (!error && need_save) {
255                        error = config.save(NameSession.gb_main, config_names[count], false);
256                    }
257                }
258                progress.inc_and_check_user_abort(error);
259            }
260        }
261        commit_progress.inc_and_check_user_abort(error);
262    }
263
264    // rename species in saved colorsets
265    if (!error) {
266        GBDATA *gb_species_colorset_root = GBT_colorset_root(NameSession.gb_main, "species");
267        if (gb_species_colorset_root) {
268            ConstStrArray colorset_names;
269            GBT_get_colorset_names(colorset_names, gb_species_colorset_root);
270
271            int colorset_count = colorset_names.size();
272            if (colorset_count>0) {
273                arb_progress progress(GBS_global_string("Correcting names in %i colorset%c", colorset_count, "s"[colorset_count<2]), colorset_count);
274
275                for (int c = 0; c<colorset_count && !error; ++c) {
276                    GBDATA *gb_colorset     = GBT_find_colorset(gb_species_colorset_root, colorset_names[c]);
277                    if (!gb_colorset) error = GB_await_error();
278                    else {
279                        ConstStrArray colorDefs;
280                        error = GBT_load_colorset(gb_colorset, colorDefs);
281                        if (!error) {
282                            StrArray modifiedDefs;
283                            bool     changed = false;
284
285                            for (int d = colorDefs.size()-1; d>=0; --d) {
286                                const char *def   = colorDefs[d];
287                                const char *equal = strchr(def, '=');
288
289                                if (equal) { // only handle correct entries (do not touch rest)
290                                    if (strcmp(equal+1, "0") == 0) { // unneeded "no color"-entry (see [14094])
291                                        colorDefs.remove(d);
292                                        changed = true;
293                                    }
294                                    else {
295                                        gbt_renamed *rns;
296                                        {
297                                            LocallyModify<char>  tempSplit(const_cast<char*>(equal)[0], 0);
298                                            rns = (gbt_renamed *)GBS_read_hash(NameSession.renamed_hash, def);
299                                        }
300                                        if (rns) { // species was renamed
301                                            char *newDef = GBS_global_string_copy("%s%s", rns->data, equal);
302                                            colorDefs.replace(d, newDef); // replace colorDefs
303                                            modifiedDefs.put(newDef);     // keep heapcopy until colorDefs gets written
304
305                                            changed = true;
306                                        }
307                                    }
308                                }
309                            }
310
311                            if (changed && !error) error = GBT_save_colorset(gb_colorset, colorDefs);
312                        }
313                    }
314                    progress.inc_and_check_user_abort(error);
315                }
316            }
317        }
318        commit_progress.inc_and_check_user_abort(error);
319    }
320
321    // rename links in pseudo-species
322    if (!error && is_genome_db) {
323        {
324            arb_progress progress("Correcting names of organism references");
325
326            GBDATA *gb_pseudo;
327            for (gb_pseudo = GEN_first_pseudo_species(NameSession.gb_main);
328                 gb_pseudo && !error;
329                 gb_pseudo = GEN_next_pseudo_species(gb_pseudo))
330            {
331                GBDATA *gb_origin_organism = GB_entry(gb_pseudo, "ARB_origin_species");
332                if (gb_origin_organism) {
333                    const char  *origin = GB_read_char_pntr(gb_origin_organism);
334                    gbt_renamed *rns    = (gbt_renamed *)GBS_read_hash(NameSession.renamed_hash, origin);
335                    if (rns) {          // species was renamed
336                        const char *newname = &rns->data[0];
337                        error               = GB_write_string(gb_origin_organism, newname);
338                    }
339                }
340            }
341        }
342        commit_progress.inc_and_check_user_abort(error);
343    }
344
345    gbt_free_rename_session_data();
346
347    error = GB_end_transaction(NameSession.gb_main, error);
348    return error;
349}
350
351// --------------------------------------------------------------------------------
352
353#ifdef UNIT_TESTS
354#ifndef TEST_UNIT_H
355#include <test_unit.h>
356#endif
357
358// #define TEST_AUTO_UPDATE // uncomment to auto-update test result db
359
360void TEST_SLOW_rename_session() {
361    const char *inputname    = "TEST_opti_ascii_in.arb";
362    const char *outputname   = "TEST_opti_ascii_renamed.arb";
363    const char *expectedname = "TEST_opti_ascii_renamed_expected.arb";
364
365    {
366        GB_shell  shell;
367        GBDATA   *gb_main;
368        TEST_EXPECT_RESULT__NOERROREXPORTED(gb_main = GB_open(inputname, "rw"));
369
370        for (int session = 1; session <= 2; ++session) {
371            TEST_ANNOTATE(GBS_global_string("session=%i", session));
372
373            TEST_EXPECT_NO_ERROR(GBT_begin_rename_session(gb_main, 0));
374            if (session == 2) { // session 1 tests renaming nothing
375                // only in config 'some':
376                TEST_EXPECT_NO_ERROR(GBT_rename_species("FrnPhilo", "olihPnrF", true));
377                TEST_EXPECT_NO_ERROR(GBT_rename_species("DsfDesul", "luseDfsD", true));
378                // also in config 'other':
379                TEST_EXPECT_NO_ERROR(GBT_rename_species("CalSacch", "hccaSlaC", true));
380                TEST_EXPECT_NO_ERROR(GBT_rename_species("LacReute", "etueRcaL", true));
381            }
382            TEST_EXPECT_NO_ERROR(GBT_commit_rename_session());
383        }
384
385        TEST_EXPECT_NO_ERROR(GB_save_as(gb_main, outputname, "a"));
386        GB_close(gb_main);
387    }
388
389#if defined(TEST_AUTO_UPDATE)
390    TEST_COPY_FILE(outputname, expectedname);
391#endif
392    TEST_EXPECT_TEXTFILE_DIFFLINES(outputname, expectedname, 0);
393    TEST_EXPECT_ZERO_OR_SHOW_ERRNO(GB_unlink(outputname));
394}
395TEST_PUBLISH(TEST_SLOW_rename_session);
396
397#endif // UNIT_TESTS
398
399// --------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.