source: branches/alilink/SL/AW_NAME/AW_rename.cxx

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