source: tags/arb-6.0-rc3/SL/AW_NAME/AW_rename.cxx

Last change on this file was 11512, checked in by westram, 10 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 23.6 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : AW_rename.cxx                                     //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "AW_rename.hxx"
12
13#include <aw_awars.hxx>
14#include <aw_window.hxx>
15#include <aw_root.hxx>
16#include <aw_question.hxx>
17#include <aw_msg.hxx>
18#include <arb_progress.h>
19
20#include <names_client.h>
21#include <servercntrl.h>
22#include <client.h>
23
24#include <cctype>
25#include <ctime>
26
27static const char *get_addid(GBDATA *gb_main) {
28    GB_transaction ta(gb_main);
29    GBDATA *gb_addid = GB_search(gb_main, AWAR_NAMESERVER_ADDID, GB_FIND);
30    return gb_addid ? GB_read_char_pntr(gb_addid) : 0;
31}
32
33static GB_ERROR set_addid(GBDATA *gb_main, const char *addid) {
34    GB_ERROR error    = GB_push_transaction(gb_main);
35    if (!error) error = GBT_write_string(gb_main, AWAR_NAMESERVER_ADDID, addid ? addid : "");
36    return GB_end_transaction(gb_main, error);
37}
38
39const char *AW_get_nameserver_addid(GBDATA *gb_main) {
40    // return the additional field used for nameserver connection
41    const char *addid = get_addid(gb_main);
42
43    aw_assert(addid); // the current DB has no entry AWAR_NAMESERVER_ADDID! (programmers error)
44    if (!addid) addid = ""; // NDEBUG fallback
45
46    return addid;
47}
48
49GB_ERROR AW_select_nameserver(GBDATA *gb_main, GBDATA *gb_other_main) {
50    // if entry AWAR_NAMESERVER_ADDID isn't defined yet, try to detect a reasonable value
51    // from arb_tcp.dat. Ask user if multiple servers are defined.
52    //
53    // if gb_other_main is defined try to use value from there.
54
55    const char *addid   = get_addid(gb_main);
56    GB_ERROR    error   = 0;
57
58    if (!addid && gb_other_main && gb_other_main != gb_main) {
59        // look whether main DB has a defined addid
60        addid = get_addid(gb_other_main);
61        set_addid(gb_main, addid);
62    }
63
64    if (!addid) {
65        const char * const *nameservers = GBS_get_arb_tcp_entries("ARB_NAME_SERVER*");
66
67        if (!nameservers) error = GB_await_error();
68        else {
69            int serverCount = 0;
70
71            for (int c = 0; nameservers[c]; c++) serverCount++;
72
73            if (serverCount == 0) {
74                error = GBS_global_string("No nameserver defined.");
75            }
76            else {
77                char **fieldNames = (char **)malloc(serverCount*sizeof(*fieldNames));
78                for (int c = 0; c<serverCount; c++) {
79                    const char *ipport = GBS_read_arb_tcp(nameservers[c]);
80                    fieldNames[c]      = nulldup(GBS_scan_arb_tcp_param(ipport, "-f")); // may return 0
81
82                    // parameter -f contains default value (e.g. '-fstart=1')
83                    if (fieldNames[c]) {
84                        char *equal = strchr(fieldNames[c], '=');
85                        if (equal) equal[0] = 0;
86                    }
87                }
88
89                if (serverCount == 1) { // exactly 1 server defined -> don't ask
90                    error = set_addid(gb_main, fieldNames[0]);
91                }
92                else { // let the user select which nameserver to use
93                    int         len     = serverCount; // commas+0term
94                    const char *nofield = "None (only 'acc')";
95
96                    for (int c = 0; c<serverCount; c++) {
97                        if (fieldNames[c]) len += strlen(fieldNames[c]);
98                        else len += strlen(nofield);
99                    }
100
101                    char *buttons = (char*)malloc(len);
102                    buttons[0]    = 0;
103                    for (int c = 0; c<serverCount; c++) {
104                        if (c) strcat(buttons, ",");
105                        strcat(buttons, fieldNames[c] ? fieldNames[c] : nofield);
106                    }
107
108                    int answer = aw_question("nameserv_select",
109                                             "Select if and which additional DB field you want to use",
110                                             buttons, false, "namesadmin.hlp");
111
112                    error = set_addid(gb_main, fieldNames[answer]);
113
114                    free(buttons);
115                }
116
117                for (int c = 0; c<serverCount; c++) free(fieldNames[c]);
118                free(fieldNames);
119            }
120        }
121    }
122
123    return error;
124}
125
126
127// ------------------------------------
128//      class NameServerConnection
129
130class NameServerConnection {
131    aisc_com   *link;
132    T_AN_LOCAL  locs;
133    T_AN_MAIN   com;
134    int         persistent;     // if true -> connection will not be closed
135    time_t      linktime;       // time, when link has been established
136
137    int init_local_com_names()
138    {
139        if (!link) return 1;    //!* create and init local com structure **
140        if (aisc_create(link, AN_MAIN, com,
141                        MAIN_LOCAL, AN_LOCAL, locs,
142                        LOCAL_WHOAMI, "i bin der arb_tree",
143                        NULL)) {
144            return 1;
145        }
146        return 0;
147    }
148
149    NameServerConnection(const NameServerConnection& other);
150    NameServerConnection& operator=(const NameServerConnection& /* other */);
151
152    GB_ERROR reconnect(GBDATA *gb_main) { // reconnect ignoring consistency
153        int old_persistent = persistent;
154
155        printf("Reconnecting name server\n");
156
157        persistent = 0; // otherwise disconnect() won't disconnect
158        disconnect();
159        persistent = old_persistent; // restore previous persistence
160
161        return connect(gb_main);
162    }
163
164    char *fieldUsedByServer(GB_ERROR& err) {
165        char *field = 0;
166        if (aisc_get(link, AN_MAIN, com,
167                     MAIN_ADD_FIELD, &field,
168                     NULL)) {
169            err = "Connection Problems with the NAME_SERVER";
170            aw_assert(field == 0);
171        }
172        return field;
173    }
174
175    GB_ERROR expectServerUsesField(const char *expected_field) {
176        GB_ERROR  err          = 0;
177        char     *server_field = fieldUsedByServer(err);
178
179        if (!err && strcmp(expected_field, server_field) != 0) {
180            err = GBS_global_string("Additional field doesn't match (expected='%s', server uses='%s')", expected_field, server_field);
181        }
182        free(server_field);
183        return err;
184    }
185
186public:
187
188    NameServerConnection() {
189        link       = 0;
190        locs.clear();
191        com.clear();
192        persistent = 0;
193    }
194    virtual ~NameServerConnection() {
195        aw_assert(persistent == 0); // forgot to remove persistence ?
196        disconnect();
197    }
198
199    GB_ERROR connect(GBDATA *gb_main) {
200        arb_progress::show_comment("Connecting to name server");
201
202        GB_ERROR err = 0;
203        if (!link) {
204            const char *add_field = AW_get_nameserver_addid(gb_main);
205            const char *server_id = GBS_nameserver_tag(add_field);
206
207            err = arb_look_and_start_server(AISC_MAGIC_NUMBER, server_id);
208
209            if (!err) {
210                const char *ipport = GBS_read_arb_tcp(server_id);
211                if (!ipport) err = GB_await_error();
212                else {
213                    link     = aisc_open(ipport, com, AISC_MAGIC_NUMBER, &err);
214                    linktime = time(0);
215
216                    if (!err) {
217                        if (init_local_com_names()) {
218                            err = GBS_global_string("Can't connect %s %s", server_id, ipport);
219                        }
220                        else {
221                            err = expectServerUsesField(add_field);
222                        }
223                    }
224                }
225            }
226        }
227        else {
228            long linkAge     = int(time(0)-linktime);
229            bool doReconnect = false;
230
231#if defined(DEBUG) && 0
232            // print information about name-server link age
233            static long lastage = -1;
234            if (linkAge != lastage) {
235                printf("Age of NameServerConnection: %li\n", linkAge);
236                lastage = linkAge;
237            }
238#endif // DEBUG
239
240            if (linkAge > (5*60)) { // perform a reconnect after 5 minutes
241                // Reason : The pipe to the name server breaks after some time
242                doReconnect = true;
243            }
244            else {
245                const char *add_field = AW_get_nameserver_addid(gb_main);
246                GB_ERROR    error     = expectServerUsesField(add_field);
247
248                if (error) {
249                    printf("Error: %s\n", error);
250                    doReconnect = true;
251                }
252            }
253
254            if (doReconnect) {
255                err = reconnect(gb_main);
256            }
257        }
258        return err;
259    }
260
261    void disconnect() {
262        if (persistent == 0) {
263            if (link) {
264                aisc_close(link, com);
265                locs.clear();
266                com.clear();
267            }
268            link = 0;
269        }
270    }
271
272    void persistence(bool persist) {
273        if (persist) {
274            ++persistent;
275        }
276        else {
277            --persistent;
278            if (persistent <= 0) {
279                persistent = 0;
280                disconnect();
281            }
282        }
283    }
284
285
286    aisc_com *getLink() { return link; }
287    const T_AN_LOCAL& getLocs() const { return locs; } 
288};
289
290static NameServerConnection name_server;
291
292PersistentNameServerConnection::PersistentNameServerConnection() {
293    name_server.persistence(true);
294}
295PersistentNameServerConnection::~PersistentNameServerConnection() {
296    name_server.persistence(false);
297}
298
299// --------------------------------------------------------------------------------
300
301GB_ERROR AW_test_nameserver(GBDATA *gb_main) {
302    GB_ERROR err = name_server.connect(gb_main);
303    return err;
304}
305
306// --------------------------------------------------------------------------------
307
308GB_ERROR AWTC_generate_one_name(GBDATA *gb_main, const char *full_name, const char *acc, const char *addid, char*& new_name) {
309    // create a unique short name for 'full_name'
310    // the result is written into 'new_name' (as malloc-copy)
311    // if fails: GB_ERROR!=0 && new_name==0
312    // acc and addid may be 0
313
314    new_name = 0;
315    if (!acc) acc = "";
316
317    arb_progress progress("Generating species ID");
318
319    GB_ERROR err = name_server.connect(gb_main);
320    if (err) return err;
321
322    static char *shrt = 0;
323    if (strlen(full_name)) {
324        if (aisc_nput(name_server.getLink(), AN_LOCAL, name_server.getLocs(),
325                      LOCAL_FULL_NAME,  full_name,
326                      LOCAL_ACCESSION,  acc,
327                      LOCAL_ADDID,      addid ? addid : "",
328                      LOCAL_ADVICE,     "",
329                      NULL)) {
330            err = "Connection Problems with the NAME_SERVER";
331        }
332        if (aisc_get(name_server.getLink(), AN_LOCAL, name_server.getLocs(),
333                     LOCAL_GET_SHORT,   &shrt,
334                     NULL)) {
335            err = "Connection Problems with the NAME_SERVER";
336        }
337    }
338
339    if (err) {
340        free(shrt);
341    }
342    else {
343        if (shrt) {
344            new_name = shrt;
345            shrt = 0;
346        }
347        else {
348            err = GB_export_errorf("Failed to generate species ID (shortname) for '%s'", full_name);
349        }
350    }
351
352    name_server.disconnect();
353
354    return err;
355}
356
357GB_ERROR AWTC_recreate_name(GBDATA *gb_species) {
358    GBDATA       *gb_main = GB_get_root(gb_species);
359    arb_progress  progress("Recreating species ID");
360    GB_ERROR      error   = name_server.connect(gb_main);
361    if (!error) {
362        const char *add_field = AW_get_nameserver_addid(gb_main);
363        char       *ali_name  = GBT_get_default_alignment(gb_main);
364
365        GBDATA *gb_name      = GB_entry(gb_species, "name");
366        GBDATA *gb_full_name = GB_entry(gb_species, "full_name");
367        GBDATA *gb_acc       = GBT_gen_accession_number(gb_species, ali_name);
368        GBDATA *gb_addfield  = add_field[0] ? GB_entry(gb_species, add_field) : 0;
369
370        char *name      = gb_name ?     GB_read_string   (gb_name)     : strdup("");
371        char *full_name = gb_full_name ? GB_read_string  (gb_full_name) : strdup("");
372        char *acc       = gb_acc ?      GB_read_string   (gb_acc)      : strdup("");
373        char *addid     = gb_addfield ? GB_read_as_string(gb_addfield) : strdup("");
374
375        long   deleted = 0;
376        char *shrt    = 0;
377
378        if (aisc_nput(name_server.getLink(), AN_LOCAL, name_server.getLocs(),
379                      LOCAL_FULL_NAME,  full_name,
380                      LOCAL_ACCESSION,  acc,
381                      LOCAL_ADDID,      addid,
382                      LOCAL_ADVICE,     "",
383                      NULL) != 0 ||
384            aisc_get(name_server.getLink(), AN_LOCAL, name_server.getLocs(),
385                     LOCAL_DEL_SHORT,   &deleted,
386                     NULL)  != 0 ||
387            aisc_get(name_server.getLink(), AN_LOCAL, name_server.getLocs(),
388                     LOCAL_GET_SHORT,   &shrt,
389                     NULL)  != 0)
390        {
391            error = "Connection Problems with the NAME_SERVER";
392        }
393        name_server.disconnect();
394
395        if (!error) {
396            GBT_begin_rename_session(gb_main, 0);
397            error = GBT_rename_species(name, shrt, true);
398            if (error) {
399                if (GBT_find_species(gb_main, shrt)) { // it was a rename error
400                    int done = 0;
401                    error    = 0;
402                    for (int count = 2; !done && !error && count<10; count++) {
403                        const char *other_short = GBS_global_string("%s.%i", shrt, count);
404                        if (!GBT_find_species(gb_main, other_short)) {
405                            error            = GBT_rename_species(name, other_short, true);
406                            if (!error) done = 1;
407                        }
408                    }
409
410                    if (!done && !error) {
411                        error = "Failed to regenerate species ID. Please use 'Species/Synchronize IDs'";
412                    }
413                }
414            }
415
416            if (error) GBT_abort_rename_session();
417            else error = GBT_commit_rename_session();
418        }
419
420        free(shrt);
421        free(addid);
422        free(acc);
423        free(full_name);
424        free(name);
425    }
426
427    return error;
428}
429
430char *AWTC_create_numbered_suffix(GB_HASH *species_name_hash, const char *shortname, GB_ERROR& warning) {
431    char *newshort = 0;
432    if (GBS_read_hash(species_name_hash, shortname)) {
433        int i;
434        newshort = (char *)GB_calloc(sizeof(char), strlen(shortname)+20);
435        for (i = 1; ; i++) {
436            sprintf(newshort, "%s.%i", shortname, i);
437            if (!GBS_read_hash(species_name_hash, newshort))break;
438        }
439
440        warning =
441            "There are duplicated species!\n"
442            "The IDs of these species ('name') contain a '.' character followed by a number.\n"
443            "We strongly recommend you try understand and solve this problem\n"
444            "(see HELP in 'Species/Synchronize IDs' window)";
445    }
446    return newshort;
447}
448
449GB_ERROR AWTC_pars_names(GBDATA *gb_main, bool *isWarningPtr) {
450    // rename species according to name_server
451    // 'isWarning' is set to true, in case of duplicates-warning
452
453    arb_progress gen_progress("Generating new names", 2);
454    GB_ERROR     err       = name_server.connect(gb_main);
455    bool         isWarning = false;
456
457    if (!err) {
458        err = GBT_begin_rename_session(gb_main, 1);
459        if (!err) {
460            char     *ali_name = GBT_get_default_alignment(gb_main);
461            long      spcount  = GBT_get_species_count(gb_main);
462            GB_HASH  *hash     = GBS_create_hash(spcount, GB_IGNORE_CASE);
463            GB_ERROR  warning  = 0;
464
465            if (spcount) {
466                arb_progress progress("Renaming species", spcount);
467                const char *add_field = AW_get_nameserver_addid(gb_main);
468
469                for (GBDATA *gb_species = GBT_first_species(gb_main);
470                     gb_species && !err;
471                     gb_species = GBT_next_species(gb_species))
472                {
473                    GBDATA *gb_name      = GB_entry(gb_species, "name");
474                    GBDATA *gb_full_name = GB_entry(gb_species, "full_name");
475                    GBDATA *gb_acc       = GBT_gen_accession_number(gb_species, ali_name);
476                    GBDATA *gb_addfield  = add_field[0] ? GB_entry(gb_species, add_field) : 0;
477
478                    char *name      = gb_name      ? GB_read_string   (gb_name)     : strdup("");
479                    char *full_name = gb_full_name ? GB_read_string   (gb_full_name) : strdup("");
480                    char *acc       = gb_acc       ? GB_read_string   (gb_acc)      : strdup("");
481                    char *addid     = gb_addfield  ? GB_read_as_string(gb_addfield) : strdup(""); // empty value will be set to default by nameserver
482
483                    char *shrt = 0;
484
485                    if (full_name[0] || acc[0] || addid[0]) {
486                        if (aisc_nput(name_server.getLink(), AN_LOCAL, name_server.getLocs(),
487                                      LOCAL_FULL_NAME,  full_name,
488                                      LOCAL_ACCESSION,  acc,
489                                      LOCAL_ADDID,      addid,
490                                      LOCAL_ADVICE,     name,
491                                      NULL)) {
492                            err = "Connection Problems with the NAME_SERVER";
493                        }
494                        if (aisc_get(name_server.getLink(), AN_LOCAL, name_server.getLocs(),
495                                     LOCAL_GET_SHORT,   &shrt,
496                                     NULL)) {
497                            err = "Connection Problems with the NAME_SERVER";
498                        }
499                    }
500                    else {
501                        shrt = strdup(name);
502                    }
503                    if (!err) {
504                        char *newshrt = AWTC_create_numbered_suffix(hash, shrt, warning);
505                        if (newshrt) freeset(shrt, newshrt);
506
507                        GBS_incr_hash(hash, shrt);
508                        err = GBT_rename_species(name, shrt, true);
509                    }
510
511                    free(shrt);
512                    free(addid);
513                    free(acc);
514                    free(full_name);
515                    free(name);
516
517                    progress.inc_and_check_user_abort(err);
518                }
519            }
520            else {
521                gen_progress.sub_progress_skipped(); // trigger skipped subcounter
522            }
523
524            if (err) GBT_abort_rename_session();
525            else err = GBT_commit_rename_session();
526
527            GBS_free_hash(hash);
528            free(ali_name);
529
530            if (!err) {
531                err = warning;
532                if (warning) isWarning = true;
533            }
534        }
535        name_server.disconnect();
536    }
537
538    if (isWarningPtr) *isWarningPtr = isWarning;
539    gen_progress.done(); // needed if err
540
541    return err;
542}
543
544
545static void awt_rename_cb(AW_window *aww, GBDATA *gb_main) {
546    GB_ERROR error = AWTC_pars_names(gb_main);
547    if (error) aw_message(error);
548    aww->get_root()->awar(AWAR_TREE_REFRESH)->touch();
549}
550
551
552AW_window *AWTC_create_rename_window(AW_root *root, GBDATA *gb_main) {
553    AW_window_simple *aws = new AW_window_simple;
554    aws->init(root, "AUTORENAME_SPECIES", "Synchronize species IDs");
555
556    aws->load_xfig("awtc/autoren.fig");
557
558    aws->callback((AW_CB0)AW_POPDOWN);
559    aws->at("close");
560    aws->create_button("CLOSE", "CLOSE", "C");
561
562    aws->callback(makeHelpCallback("sp_rename.hlp"));
563    aws->at("help");
564    aws->create_button("HELP", "HELP", "H");
565
566    aws->at("go");
567    aws->highlight();
568    aws->callback(makeWindowCallback(awt_rename_cb, gb_main));
569    aws->create_button("GO", "GO", "G");
570
571    return (AW_window *)aws;
572}
573
574UniqueNameDetector::UniqueNameDetector(GBDATA *gb_item_data, int additionalEntries) {
575    hash = GBS_create_hash(GB_number_of_subentries(gb_item_data)+additionalEntries, GB_IGNORE_CASE);
576
577    for (GBDATA *gb_item = GB_child(gb_item_data); gb_item; gb_item = GB_nextChild(gb_item)) {
578        GBDATA *gb_name = GB_entry(gb_item, "name");
579        if (gb_name) { // item has name -> insert to hash
580            GBS_write_hash(hash, GB_read_char_pntr(gb_name), 1);
581        }
582    }
583}
584
585UniqueNameDetector::~UniqueNameDetector() { GBS_free_hash(hash); }
586
587static char *makeUniqueShortName(const char *prefix, UniqueNameDetector& existing) {
588    // generates a non-existing short-name (name starts with prefix)
589    //
590    // returns NULL if it fails
591
592    char *result     = 0;
593    int   prefix_len = strlen(prefix);
594
595    aw_assert(prefix_len<8); // prefix has to be shorter than 8 chars!
596    if (prefix_len<8) {
597        const int max_nums[8] = { 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10 };
598        static int next_try[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
599
600        int  max_num = max_nums[prefix_len];
601        char short_name[9];
602        strcpy(short_name, prefix);
603
604        char *dig_pos = short_name+prefix_len;
605        int   num     = next_try[prefix_len];
606        int   stop    = num ? num-1 : max_num;
607
608        while (num != stop) {
609            sprintf(dig_pos, "%i", num);
610            ++num;
611            if (!existing.name_known(short_name)) {
612                result = strdup(short_name);
613                break;
614            }
615            if (num == max_num && stop != max_num) num = 0;
616        }
617        if (num == max_num) num = 0;
618        next_try[prefix_len] = num;
619    }
620    return result;
621}
622
623char *AWTC_makeUniqueShortName(const char *prefix, UniqueNameDetector& existingNames) {
624    // generates a unique species name from prefix
625    // (prefix will be fillup with zero digits and then shortened down to first char)
626    //
627    // returns NULL if failed (and exports error)
628
629    int  len = strlen(prefix);
630    char p[9];
631    strncpy(p, prefix, 8);
632
633    if (len>8) len = 8;
634    else {
635        if (len == 0) p[len++] = 'x'; // don't use digit as first character
636        while (len<8) p[len++] = '0';
637    }
638
639    p[len] = 0;
640
641    char *result = 0;
642
643    for (int l = len-1; l>0 && !result; --l) {
644        p[l]   = 0;
645        result = makeUniqueShortName(p, existingNames);
646    }
647
648    aw_assert(!result || strlen(result) <= 8);
649    if (!result) GB_export_errorf("Failed to create unique species ID (prefix='%s')", prefix);
650
651    return result;
652}
653
654char *AWTC_generate_random_name(UniqueNameDetector& existingNames) {
655    char *new_species_name = 0;
656    char  short_name[9];
657    int   count            = 10000;
658
659    short_name[8] = 0;
660    while (count--) {
661        short_name[0] = 'a'+GB_random(26); // first character has to be alpha
662
663        for (int x=1; x<8; ++x) {
664            int r = GB_random(36); // rest may be alphanumeric
665            short_name[x] = r<10 ? ('0'+r) : ('a'+r-10);
666        }
667
668        if (!existingNames.name_known(short_name)) {
669            new_species_name = strdup(short_name);
670            break;
671        }
672    }
673
674    if (!new_species_name) {
675        aw_message("Failed to generate a random name - retrying (this might hang forever)");
676        return AWTC_generate_random_name(existingNames);
677    }
678
679    return new_species_name;
680}
681
682int AWTC_name_quality(const char *short_name) {
683    // result 0 = ok for external tools
684    //        1 = ok for ARB
685    //        2 = not ok
686
687    int len         = -1;
688    int alnum_count = 0;
689    int ascii_count = 0;
690
691    while (char c = short_name[++len]) {
692        alnum_count += (isalnum(c) != 0);
693        ascii_count += (c > 32 && c < 127);
694    }
695
696    if (len>0) {
697        if (len <= 8) {
698            if (len == alnum_count) return 0; // ok for external programs
699        }
700        if (len == ascii_count) return 1; // ok for ARB
701    }
702    return 2; // not ok
703}
Note: See TracBrowser for help on using the repository browser.