source: branches/properties/AWTI/AWTI_import.cxx

Last change on this file was 19365, checked in by westram, 18 months ago
  • string splitters:
    • unittests for GBT_split_string:
      • add tests for dropEmptyTokens.
      • document special behavior for empty string.
    • define enum SplitMode. use enum instead of bool param for GBT_splitNdestroy_string + GBT_split_string.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 59.0 KB
Line 
1// ============================================================= //
2//                                                               //
3//   File      : AWTI_import.cxx                                 //
4//   Purpose   :                                                 //
5//                                                               //
6//   Institute of Microbiology (Technical University Munich)     //
7//   http://www.arb-home.de/                                     //
8//                                                               //
9// ============================================================= //
10
11#include "awti_imp_local.hxx"
12#include "awti_edit.hxx"
13
14#include <seqio.hxx>
15#include <awt.hxx>
16#include <GenomeImport.h>
17#include <GEN.hxx>
18
19#include <FileWatch.h>
20#include <item_sel_list.h>
21
22#include <aw_advice.hxx>
23#include <aw_file.hxx>
24#include <AW_rename.hxx>
25#include <aw_msg.hxx>
26#include <aw_question.hxx>
27#include <macros.hxx>
28
29#include <adGene.h>
30#include <arb_progress.h>
31#include <arb_strbuf.h>
32#include <arb_str.h>
33#include <gb_aci.h>
34
35#include <climits>
36#include <unistd.h>
37
38#include <db_scanner.hxx>
39using namespace std;
40using namespace SEQIO;
41using namespace FieldTransfer;
42
43#define MAX_COMMENT_LINES 2000
44
45
46inline const char *name_only(const char *fullpath) {
47    const char *lslash = strrchr(fullpath, '/');
48    return lslash ? lslash+1 : fullpath;
49}
50
51
52inline GB_ERROR not_in_match_error(const char *cmd) {
53    return GBS_global_string("Command '%s' may only appear after 'MATCH'", cmd);
54}
55
56inline bool wants_import_genome(AW_root *awr) { return awr->awar(AWAR_IMPORT_GENOM_DB)->read_int() == IMP_GENOME_FLATFILE; }
57
58static char *encode_escaped_chars(char *com) {
59    // Encodes the escape sequences "\\n", "\\t" and "\\0" to their corresponding characters
60    // Other occurrences of "\\" are removed.
61
62    char *result, *s, *d;
63    int   ch;
64
65    s = d = result = ARB_strdup(com);
66    while ((ch = *(s++))) {
67        switch (ch) {
68            case '\\':
69                ch = *(s++); if (!ch) { s--; break; };
70                switch (ch) {
71                    case 'n':   *(d++) = '\n'; break;
72                    case 't':   *(d++) = '\t'; break;
73                    case '0':   *(d++) = '\0'; break;
74                    default:    *(d++) = ch; break;
75                }
76                break;
77            default:
78                *(d++) = ch;
79        }
80    }
81    *d = 0;
82    return result;
83}
84
85static GB_ERROR read_import_format(const char *fullfile, import_format *ifo, bool *var_set, bool included) {
86    GB_ERROR  error = NULp;
87    FILE     *in    = fopen(fullfile, "rt");
88
89    import_match *m = ifo->match;
90
91    if (!in) {
92        const char *name = name_only(fullfile);
93
94        if (included) {
95            error = GB_export_IO_error("including", name);
96        }
97        else {
98            error = strchr(name, '*')
99                ? "Please use 'AUTO DETECT' or manually select an import format"
100                : GB_IO_error("loading import filter", name);
101        }
102    }
103    else {
104        char   *s1, *s2;
105        size_t  lineNumber    = 0;
106        bool    include_error = false;
107
108        while (!error && read_string_pair(in, s1, s2, lineNumber)) {
109
110#define GLOBAL_COMMAND(cmd) (!error && strcmp(s1, cmd) == 0)
111#define MATCH_COMMAND(cmd)  (GLOBAL_COMMAND(cmd) && (m || !(error = not_in_match_error(cmd))))
112
113            if (GLOBAL_COMMAND("MATCH")) {
114                m = new import_match;
115
116                m->defined_at = GBS_global_string_copy("%zi,%s", lineNumber, name_only(fullfile));
117                m->next       = ifo->match; // this concatenates the filters to the front -> the list is reversed below
118                ifo->match    = m;
119                m->match      = encode_escaped_chars(s2);
120                m->type       = GB_STRING;
121
122                if (ifo->autotag) m->mtag = ARB_strdup(ifo->autotag); // will be overwritten by TAG command
123            }
124            else if (MATCH_COMMAND("SRT"))         { reassign(m->srt, s2); }
125            else if (MATCH_COMMAND("ACI"))         { reassign(m->aci, s2); }
126            else if (MATCH_COMMAND("WRITE"))       { reassign(m->write, s2); }
127            else if (MATCH_COMMAND("WRITE_INT"))   { reassign(m->write, s2); m->type = GB_INT; }
128            else if (MATCH_COMMAND("WRITE_FLOAT")) { reassign(m->write, s2); m->type = GB_FLOAT; }
129            else if (MATCH_COMMAND("APPEND"))      { reassign(m->append, s2); }
130            else if (MATCH_COMMAND("SETVAR")) {
131                if (strlen(s2) != 1 || s2[0]<'a' || s2[0]>'z') {
132                    error = "Allowed variable names are a-z";
133                }
134                else {
135                    var_set[s2[0]-'a'] = true;
136                    reassign(m->setvar, s2);
137                }
138            }
139            else if (MATCH_COMMAND("TAG")) {
140                if (s2[0]) reassign(m->mtag, s2);
141                else error = "Empty TAG is not allowed";
142            }
143            else if (GLOBAL_COMMAND("AUTODETECT"))               { reassign(ifo->autodetect,    s2); }
144            else if (GLOBAL_COMMAND("SYSTEM"))                   { reassign(ifo->system,        s2); }
145            else if (GLOBAL_COMMAND("NEW_FORMAT"))               { reassign(ifo->new_format,    s2); ifo->new_format_lineno = lineNumber; }
146            else if (GLOBAL_COMMAND("BEGIN"))                    { reassign(ifo->begin,         s2); }
147            else if (GLOBAL_COMMAND("FILETAG"))                  { reassign(ifo->filetag,       s2); }
148            else if (GLOBAL_COMMAND("SEQUENCESRT"))              { reassign(ifo->sequencesrt,   s2); }
149            else if (GLOBAL_COMMAND("SEQUENCEACI"))              { reassign(ifo->sequenceaci,   s2); }
150            else if (GLOBAL_COMMAND("SEQUENCEEND"))              { reassign(ifo->sequenceend,   s2); }
151            else if (GLOBAL_COMMAND("END"))                      { reassign(ifo->end,           s2); } // @@@ has no effect -> silently ignore (no need to store)
152            else if (GLOBAL_COMMAND("AUTOTAG"))                  { reassign(ifo->autotag,       s2); }
153            else if (GLOBAL_COMMAND("SEQUENCESTART"))            { reassign(ifo->sequencestart, s2); ifo->read_this_sequence_line_too = 1; }
154            else if (GLOBAL_COMMAND("SEQUENCEAFTER"))            { reassign(ifo->sequencestart, s2); ifo->read_this_sequence_line_too = 0; }
155            else if (GLOBAL_COMMAND("KEYWIDTH"))                 { ifo->tab            = atoi(s2); }
156            else if (GLOBAL_COMMAND("SEQUENCECOLUMN"))           { ifo->sequencecolumn = atoi(s2); }
157            else if (GLOBAL_COMMAND("CREATE_ACC_FROM_SEQUENCE")) { ifo->autocreateacc  = 1; }
158            else if (GLOBAL_COMMAND("DONT_GEN_NAMES"))           { ifo->noautonames    = 1; }
159            else if (GLOBAL_COMMAND("DESCRIPTION"))              { appendTo(ifo->description, '\n', s2); }
160            else if (GLOBAL_COMMAND("INCLUDE")) {
161                char *dir         = AW_extract_directory(fullfile);
162                char *includeFile = GBS_global_string_copy("%s/%s", dir, s2);
163
164                error = read_import_format(includeFile, ifo, var_set, true);
165                if (error) include_error = true;
166
167                free(includeFile);
168                free(dir);
169            }
170            else {
171                bool ifnotset  = GLOBAL_COMMAND("IFNOTSET");
172                bool setglobal = GLOBAL_COMMAND("SETGLOBAL");
173
174                if (ifnotset || setglobal) {
175                    if (s2[0]<'a' || s2[0]>'z') {
176                        error = "Allowed variable names are a-z";
177                    }
178                    else {
179                        int off = 1;
180                        while (isblank(s2[off])) off++;
181                        if (!s2[off]) error = GBS_global_string("Expected two arguments in '%s'", s2);
182                        else {
183                            char        varname = s2[0];
184                            const char *arg2    = s2+off;
185
186                            if (ifnotset) {
187                                if (ifo->variable_errors.get(varname)) {
188                                    error = GBS_global_string("Redefinition of IFNOTSET %c", varname);
189                                }
190                                else {
191                                    ifo->variable_errors.set(varname, arg2);
192                                }
193                            }
194                            else {
195                                awti_assert(setglobal);
196                                ifo->global_variables.set(varname, arg2);
197                                var_set[varname-'a'] = true;
198                            }
199                        }
200                    }
201                }
202                else if (!error) {
203                    error = GBS_global_string("Unknown command '%s'", s1);
204                }
205            }
206
207            free(s1);
208            free(s2);
209        }
210
211        // check if required fields are defined
212        if (!error && !included) { // do not run checks for included part (allowed to be incomplete)
213            if (ifo->system || ifo->new_format) {
214                if      (!ifo->system)     error = "Missing command SYSTEM (needed with NEW_FORMAT)";
215                else if (!ifo->new_format) error = "Missing command NEW_FORMAT (needed with SYSTEM)";
216
217                if (!error && ifo->sequencestart) {
218                    error = "SEQUENCESTART/SEQUENCEAFTER cannot be used together with NEW_FORMAT";
219                }
220            }
221            else {
222                if (!ifo->sequencestart) {
223                    error = "neither SEQUENCESTART nor SEQUENCEAFTER specified (need one of them)";
224                }
225            }
226        }
227
228        if (error) {
229            error = GBS_global_string("%sin line %zi of %s '%s':\n%s",
230                                      include_error ? "included " : "",
231                                      lineNumber,
232                                      included ? "file" : "import format",
233                                      name_only(fullfile),
234                                      error);
235        }
236
237        fclose(in);
238
239#undef MATCH_COMMAND
240#undef GLOBAL_COMMAND
241    }
242
243    return error;
244}
245
246GB_ERROR ArbImporter::read_format(const char *file) {
247    char *fullfile = ARB_strdup(GB_path_in_ARBHOME(file));
248
249    delete ifo;
250    ifo = new import_format;
251
252    bool var_set[IFS_VARIABLES];
253    for (int i = 0; i<IFS_VARIABLES; i++) var_set[i] = false;
254
255    GB_ERROR error = read_import_format(fullfile, ifo, var_set, false);
256
257
258    for (int i = 0; i<IFS_VARIABLES && !error; i++) {
259        bool ifnotset = ifo->variable_errors.get(i+'a');
260        if (var_set[i]) {
261            bool isglobal = ifo->global_variables.get(i+'a');
262            if (!ifnotset && !isglobal) { // don't warn if variable is global
263                error = GBS_global_string("Warning: missing IFNOTSET for variable '%c'", 'a'+i);
264            }
265        }
266        else {
267            if (ifnotset) {
268                error = GBS_global_string("Warning: useless IFNOTSET for unused variable '%c'", 'a'+i);
269            }
270        }
271    }
272
273    // reverse order of match list (was appended backwards during creation)
274    if (ifo->match) ifo->match = ifo->match->reverse(NULp);
275
276    free(fullfile);
277
278    return error;
279}
280
281import_match::import_match() :
282    match(NULp),
283    aci(NULp),
284    srt(NULp),
285    mtag(NULp),
286    append(NULp),
287    write(NULp),
288    setvar(NULp),
289    type(GB_NONE),
290    defined_at(NULp),
291    next(NULp)
292{}
293
294import_match::~import_match() {
295    free(match);
296    free(aci);
297    free(srt);
298    free(mtag);
299    free(append);
300    free(write);
301    free(setvar);
302    free(defined_at);
303
304    delete next;
305}
306
307import_format::import_format() :
308    autodetect(NULp),
309    system(NULp),
310    new_format(NULp),
311    new_format_lineno(0),
312    tab(0),
313    description(NULp),
314    begin(NULp),
315    sequencestart(NULp),
316    read_this_sequence_line_too(0),
317    sequenceend(NULp),
318    sequencesrt(NULp),
319    sequenceaci(NULp),
320    filetag(NULp),
321    autotag(NULp),
322    sequencecolumn(0),
323    autocreateacc(0),
324    noautonames(0),
325    end(NULp),
326    b1(NULp),
327    b2(NULp),
328    match(NULp)
329{}
330
331import_format::~import_format() {
332    free(autodetect);
333    free(system);
334    free(new_format);
335    free(description);
336    free(begin);
337    free(sequencestart);
338    free(sequenceend);
339    free(sequencesrt);
340    free(sequenceaci);
341    free(filetag);
342    free(autotag);
343    free(end);
344    free(b1);
345    free(b2);
346
347    delete match;
348}
349
350
351static int cmp_ift(const void *p0, const void *p1, void *) {
352    return ARB_stricmp((const char *)p0, (const char *)p1);
353}
354
355inline bool is_hidden_file(const char *file) {
356    const char *name  = strrchr(file, '/');
357    name              = name ? name+1 : file;
358    return name[0]   == '.';
359}
360
361static void removeHiddenFiles(StrArray& files) {
362    for (int i = files.size()-1; i >= 0; --i) {
363        if (is_hidden_file(files[i])) {
364            files.remove(i);
365        }
366    }
367}
368
369void ArbImporter::detect_format(AW_root *root) {
370    StrArray files;
371    {
372        AW_awar       *awar_dirs = root->awar(AWAR_IMPORT_FORMATDIR);
373        ConstStrArray  dirs;
374        GBT_split_string(dirs, awar_dirs->read_char_pntr(), ":", SPLIT_DROPEMPTY);
375        for (unsigned i = 0; i<dirs.size(); ++i) GBS_read_dir(files, dirs[i], "*.ift");
376        removeHiddenFiles(files);
377    }
378    files.sort(cmp_ift, NULp);
379
380    char     buffer[AWTI_IMPORT_CHECK_BUFFER_SIZE+10];
381    GB_ERROR error = NULp;
382
383    int matched       = -1;
384    int first         = -1;
385    int matched_count = 0;
386
387    AW_awar *awar_filter   = root->awar(AWAR_IMPORT_FORMATNAME);
388    char    *prev_selected = awar_filter->read_string();
389
390    // read start of (1st) file to import into 'buffer'
391    {
392        FILE *test = NULp;
393        {
394            char *f = root->awar(AWAR_IMPORT_FILENAME)->read_string();
395
396            if (f[0]) {
397                const char *com = GBS_global_string("cat %s 2>/dev/null", f);
398                test            = popen(com, "r");
399            }
400            free(f);
401            if (!test) error = "No input file specified -> cannot detect anything";
402        }
403        if (test) {
404            int size = fread(buffer, 1, AWTI_IMPORT_CHECK_BUFFER_SIZE, test);
405            pclose(test);
406            if (size>=0) buffer[size] = 0;
407        }
408        else {
409            buffer[0] = 0;
410        }
411    }
412
413    for (int idx = 0; files[idx] && !error; ++idx) {
414        const char *filtername = files[idx];
415        awar_filter->write_string(filtername);
416
417        GB_ERROR form_err = read_format(filtername);
418        if (form_err) {
419            aw_message(form_err);
420        }
421        else {
422            if (ifo->autodetect) {      // detectable
423                char *autodetect = encode_escaped_chars(ifo->autodetect);
424                if (GBS_string_matches(buffer, autodetect, GB_MIND_CASE)) {
425                    // format found!
426                    matched_count++;
427                    if (matched == -1) matched = idx; // remember first/next found format
428                    if (first == -1) first     = idx; // remember first found format
429
430                    if (strcmp(filtername, prev_selected) == 0) { // previously selected filter
431                        matched = -1; // select next (or first.. see below)
432                    }
433                }
434                free(autodetect);
435            }
436        }
437
438        delete ifo; ifo = NULp;
439    }
440
441    const char *select = NULp;
442    if (!error) {
443        switch (matched_count) {
444            case 0:
445                AW_advice("Not all formats can be auto-detected.\n"
446                          "Some need to be selected manually.",
447                          AW_ADVICE_TOGGLE,
448                          "No format auto-detected",
449                          "arb_import.hlp");
450
451                select = "unknown.ift";
452                break;
453
454            default:
455                AW_advice("Several import filters matched during auto-detection.\n"
456                          "Click 'AUTO DETECT' again to select next matching import-filter.",
457                          AW_ADVICE_TOGGLE,
458                          "Several formats detected",
459                          "arb_import.hlp");
460
461                // fall-through
462            case 1:
463                if (matched == -1) {
464                    // wrap around to top (while searching next matching filter)
465                    // or re-select the one matching filter (if it was previously selected)
466                    awti_assert(first != -1);
467                    matched = first;
468                }
469                awti_assert(matched != -1);
470                select = files[matched];  // select 1st matching filter
471                break;
472        }
473    }
474    else {
475        select = prev_selected;
476        aw_message(error);
477    }
478
479    awar_filter->write_string(select);
480    free(prev_selected);
481}
482
483int ArbImporter::next_file() {
484    if (in) fclose(in);
485    if (current_file_idx<0) current_file_idx = 0;
486
487    int result = 1;
488    while (result == 1 && filenames[current_file_idx]) {
489        const char *origin_file_name = filenames[current_file_idx++];
490
491        const char *sorigin   = strrchr(origin_file_name, '/');
492        if (!sorigin) sorigin = origin_file_name;
493        else sorigin++;
494
495        GB_ERROR  error          = NULp;
496        char     *mid_file_name  = NULp;
497        char     *dest_file_name = NULp;
498
499        if (ifo2 && ifo2->system) {
500            {
501                const char *sorigin_nameonly            = strrchr(sorigin, '/');
502                if (!sorigin_nameonly) sorigin_nameonly = sorigin;
503
504                char *mid_name = GB_unique_filename(sorigin_nameonly, "tmp");
505                mid_file_name  = GB_create_tempfile(mid_name);
506                free(mid_name);
507
508                if (!mid_file_name) error = GB_await_error();
509            }
510
511            if (!error) {
512                char *srt = GBS_global_string_copy("$<=%s:$>=%s", origin_file_name, mid_file_name);
513                char *sys = GBS_string_eval(ifo2->system, srt);
514
515                arb_progress::show_comment(GBS_global_string("exec '%s'", ifo2->system));
516
517                error                        = GBK_system(sys);
518                if (!error) origin_file_name = mid_file_name;
519
520                free(sys);
521                free(srt);
522            }
523        }
524
525        if (!error && ifo->system) {
526            {
527                const char *sorigin_nameonly            = strrchr(sorigin, '/');
528                if (!sorigin_nameonly) sorigin_nameonly = sorigin;
529
530                char *dest_name = GB_unique_filename(sorigin_nameonly, "tmp");
531                dest_file_name  = GB_create_tempfile(dest_name);
532                free(dest_name);
533
534                if (!dest_file_name) error = GB_await_error();
535            }
536
537            awti_assert(dest_file_name || error);
538
539            if (!error) {
540                char *srt = GBS_global_string_copy("$<=%s:$>=%s", origin_file_name, dest_file_name);
541                char *sys = GBS_string_eval(ifo->system, srt);
542
543                arb_progress::show_comment(GBS_global_string("Converting File %s", ifo->system));
544
545                error                        = GBK_system(sys);
546                if (!error) origin_file_name = dest_file_name;
547
548                free(sys);
549                free(srt);
550            }
551        }
552
553        if (!error) {
554            in = fopen(origin_file_name, "r");
555
556            if (in) {
557                result = 0;
558            }
559            else {
560                error = GBS_global_string("Error: Cannot open file %s\n", origin_file_name);
561            }
562        }
563
564        if (mid_file_name) {
565            awti_assert(GB_is_privatefile(mid_file_name, false));
566            GB_unlink_or_warn(mid_file_name, &error);
567            free(mid_file_name);
568        }
569        if (dest_file_name) {
570            GB_unlink_or_warn(dest_file_name, &error);
571            free(dest_file_name);
572        }
573
574        if (error) aw_message(error);
575    }
576
577    return result;
578}
579
580char *ArbImporter::read_line(int tab, char *sequencestart, char *sequenceend) {
581    /* two modes:   tab == 0 -> read single lines,
582       different files are separated by sequenceend,
583       tab != 0 join lines that start after position tab,
584       joined lines are separated by '|'
585       except lines that match sequencestart
586       (they may be part of sequence if read_this_sequence_line_too = 1 */
587
588    static char *in_queue  = NULp; // read data
589    static int   b2offset  = 0;
590    const int    BUFSIZE   = 8000;
591    const char  *SEPARATOR = "|";  // line separator
592
593    if (!ifo->b1) ARB_calloc(ifo->b1, BUFSIZE);
594    if (!ifo->b2) ARB_calloc(ifo->b2, BUFSIZE);
595
596    if (!in) {
597        if (next_file()) {
598            if (in_queue) {
599                char *s = in_queue;
600                in_queue = NULp;
601                return s;
602            }
603            return NULp;
604        }
605    }
606
607
608    if (!tab) {
609        if (in_queue) {
610            char *s = in_queue;
611            in_queue = NULp;
612            return s;
613        }
614        char *p = fgets_smartLF(ifo->b1, BUFSIZE-3, in);
615        if (!p) {
616            awti_assert(sequenceend); // @@@ what happens if SEQUENCEEND not specified? (print "(null)")
617            sprintf(ifo->b1, "%s", sequenceend);
618            if (in) { fclose(in); in = NULp; }
619            p = ifo->b1;
620        }
621        int len = strlen(p)-1;
622        while (len>=0) {
623            if (p[len] == '\n' || p[len] == 13) p[len--] = 0;
624            else break;
625        }
626        return ifo->b1;
627    }
628
629    b2offset = 0;
630    ifo->b2[0] = 0;
631
632    if (in_queue) {
633        b2offset = 0;
634        strncpy(ifo->b2+b2offset, in_queue, BUFSIZE - 4- b2offset);
635        b2offset += strlen(ifo->b2+b2offset);
636        in_queue = NULp;
637        if (GBS_string_matches(ifo->b2, sequencestart, GB_MIND_CASE)) return ifo->b2;
638    }
639    while (1) {
640        char *p = fgets_smartLF(ifo->b1, BUFSIZE-3, in);
641        if (!p) {
642            if (in) { fclose(in); in = NULp; }
643            break;
644        }
645        int len = strlen(p)-1;
646        while (len>=0) {
647            if (p[len] == '\n' || p[len] == 13) p[len--] = 0;
648            else break;
649        }
650
651
652        if (GBS_string_matches(ifo->b1, sequencestart, GB_MIND_CASE)) {
653            in_queue = ifo->b1;
654            return ifo->b2;
655        }
656
657        int i;
658        for (i=0; i<=tab; i++) if (ifo->b1[i] != ' ') break;
659
660        if (i < tab) {
661            in_queue = ifo->b1;
662            return ifo->b2;
663        }
664        strncpy(ifo->b2+b2offset, SEPARATOR, BUFSIZE - 4- b2offset);
665        b2offset += strlen(ifo->b2+b2offset);
666
667        p = ifo->b1;
668        if (b2offset>0) while (*p==' ') p++;    // delete spaces in second line
669
670        strncpy(ifo->b2+b2offset, p, BUFSIZE - 4- b2offset);
671        b2offset += strlen(ifo->b2+b2offset);
672    }
673    in_queue = NULp;
674    return ifo->b2;
675}
676
677static void write_entry(GBDATA *gb_main, GBDATA *gbd, const char *key, const char *str, const char *tag, int append, GB_TYPES type) {
678    if (!gbd) return;
679
680    const int len_str = strlen(str);
681    {
682        while (str[0] == ' ' || str[0] == '\t' || str[0] == '|') str++;
683        int i = len_str-1;
684        while (i >= 0 && (str[i] == ' ' || str[i] == '\t' || str[i] == '|' || str[i] == 13)) {
685            i--;
686        }
687
688        if (i<0) return;
689
690        i++;
691        if (str[i]) { // need to cut trailing whitespace?
692            char *copy = ARB_strndup(str, i);
693            write_entry(gb_main, gbd, key, copy, tag, append, type);
694            free(copy);
695            return;
696        }
697    }
698
699    GBDATA *gbk = GB_entry(gbd, key);
700
701    if (type != GB_STRING) {
702        if (!gbk) gbk=GB_create(gbd, key, type);
703        switch (type) {
704            case GB_INT:
705                GB_write_int(gbk, atoi(str));
706                break;
707            case GB_FLOAT:
708                GB_write_float(gbk, GB_atof(str));
709                break;
710            default:
711                awti_assert(0);
712                break;
713        }
714        return;
715    }
716
717    const int len_tag_incl_brackets = tag ? strlen(tag)+2 : 0;
718    if (!gbk || !append) {
719        if (!gbk) gbk=GB_create(gbd, key, GB_STRING);
720
721        if (tag) {
722            GBS_strstruct val(len_tag_incl_brackets+len_str+3);
723            val.put('[');
724            val.cat(tag);
725            val.cat("] ");
726            val.cat(str);
727            GB_write_string(gbk, val.get_data());
728        }
729        else {
730            if (strcmp(key, "name") == 0) {
731                char *nstr = GBT_create_unique_species_name(gb_main, str);
732                GB_write_string(gbk, nstr);
733                free(nstr);
734            }
735            else {
736                GB_write_string(gbk, str);
737            }
738        }
739        return;
740    }
741
742    const char *strin = GB_read_char_pntr(gbk);
743
744    const int  len = len_str + strlen(strin);
745    char      *buf = ARB_calloc<char>(len+2+len_tag_incl_brackets+1);
746
747    if (tag) {
748        char *regexp = ARB_alloc<char>(len_tag_incl_brackets+3);
749        sprintf(regexp, "*[%s]*", tag);
750
751        if (!GBS_string_matches(strin, regexp, GB_IGNORE_CASE)) { // if tag does not exist yet
752            sprintf(buf, "%s [%s] %s", strin, tag, str); // prefix with tag
753        }
754        free(regexp);
755    }
756
757    if (buf[0] == 0) {
758        sprintf(buf, "%s %s", strin, str);
759    }
760
761    GB_write_string(gbk, buf);
762    free(buf);
763    return;
764}
765
766static string expandSetVariables(const SetVariables& variables, const string& source, bool& error_occurred, const import_format *ifo) {
767    string                 dest;
768    string::const_iterator norm_start = source.begin();
769    string::const_iterator p          = norm_start;
770    error_occurred                    = false;
771
772    while (p != source.end()) {
773        if (*p == '$') {
774            ++p;
775            if (*p == '$') { // '$$' -> '$'
776                dest.append(1, *p);
777            }
778            else { // real variable
779                const string *value = variables.get(*p);
780                if (!value) {
781                    const string *user_error = ifo->variable_errors.get(*p);
782
783                    char *error = NULp;
784                    if (user_error) {
785                        error = GBS_global_string_copy("%s (variable '$%c' not set yet)", user_error->c_str(), *p);
786                    }
787                    else {
788                        error = GBS_global_string_copy("Variable '$%c' not set (missing SETVAR or SETGLOBAL?)", *p);
789                    }
790
791                    dest.append(GBS_global_string("<%s>", error));
792                    GB_export_error(error);
793                    free(error);
794                    error_occurred = true;
795                }
796                else {
797                    dest.append(*value);
798                }
799            }
800            ++p;
801        }
802        else {
803            dest.append(1, *p);
804            ++p;
805        }
806    }
807    return dest;
808}
809GB_ERROR ArbImporter::read_data(char *ali_name, int security_write, FieldTransfer::RuleSetPtr ruleset) { // @@@ split function
810    awti_assert(ifo->sequencestart); // otherwise format is invalid (assertion is no strict guarantee for validity!)
811
812    char *p;
813    while (1) { // search start of entry
814        p = read_line(0, ifo->sequencestart, ifo->sequenceend);
815        if (!p || !ifo->begin || GBS_string_matches(p, ifo->begin, GB_MIND_CASE)) break;
816    }
817    if (!p) return "Cannot find start of file: Wrong format or empty file";
818
819    static int counter       = 0; // note: dont reset counter (used for field 'name', improves uniqueness if no nameserver is used)
820    const  int start_counter = counter;
821
822    GBDATA  *gb_species_data = GBT_get_species_data(gb_import_main);
823    GBL_env  env(gb_import_main, NULp);
824
825    while (p) {
826        SetVariables variables(ifo->global_variables);
827
828        counter++;
829        const int  rel_counter = counter-start_counter;
830        GBDATA    *gb_species  = GB_create_container(gb_species_data, "species");
831
832        {
833            char text[100];
834            if (rel_counter % 10 == 0) {
835                sprintf(text, "Reading species %i", rel_counter);
836                arb_progress::show_comment(text);
837            }
838
839            sprintf(text, "spec%i", counter);
840            GBT_readOrCreate_char_pntr(gb_species, "name", text); // set default if missing
841        }
842
843        if (filenames[1]) {      // multiple files !!!
844            const char *f = strrchr(filenames[current_file_idx-1], '/');
845            if (!f) f = filenames[current_file_idx-1];
846            else f++;
847            write_entry(gb_import_main, gb_species, "file", f, ifo->filetag, 0, GB_STRING);
848        }
849
850        static bool  never_warn = false;
851        int          max_line   = never_warn ? INT_MAX : MAX_COMMENT_LINES;
852        GBL_call_env callEnv(gb_species, env);
853
854        for (int line=0; line<=max_line; line++) {
855            if (line == max_line) {
856                char *msg = GBS_global_string_copy("A database entry in file '%s' is longer than %i lines.\n"
857                                                   " * possible reasons: wrong input format or very long comment/data\n"
858                                                   " * please examine imported data if you decide to continue\n",
859                                                   filenames[current_file_idx] ? filenames[current_file_idx] : "<unknown>",
860                                                   line);
861
862                switch (aw_question("import_long_lines", msg, "Continue Reading,Continue Reading (Never ask again),Abort")) {
863                    case 0:
864                        max_line *= 2;
865                        break;
866                    case 1:
867                        max_line = INT_MAX;
868                        never_warn = true;
869                        break;
870                    case 2:
871                        break;
872                }
873
874                free(msg);
875            }
876            GB_ERROR error = NULp;
877            if (strlen(p) > ifo->tab) {
878                for (import_match *match = ifo->match; !error && match; match=match->next) {
879                    const char *what_error = NULp;
880                    if (GBS_string_matches(p, match->match, GB_MIND_CASE)) {
881                        char *dup = p+ifo->tab;
882                        while (*dup == ' ' || *dup == '\t') dup++;
883
884                        char *s    = NULp;
885                        char *dele = NULp;
886
887                        if (match->srt) {
888                            bool   err_flag;
889                            string expanded = expandSetVariables(variables, match->srt, err_flag, ifo);
890                            if (err_flag) error = GB_await_error();
891                            else {
892                                dele           = s = GBS_string_eval_in_env(dup, expanded.c_str(), callEnv);
893                                if (!s) error  = GB_await_error();
894                            }
895                            if (error) what_error = "SRT";
896                        }
897                        else {
898                            s = dup;
899                        }
900
901                        if (!error && match->aci) {
902                            bool   err_flag;
903                            string expanded     = expandSetVariables(variables, match->aci, err_flag, ifo);
904                            if (err_flag) error = GB_await_error();
905                            else {
906                                dup  = dele;
907                                dele = s = GB_command_interpreter_in_env(s, expanded.c_str(), callEnv);
908                                if (!s) error = GB_await_error();
909                                free(dup);
910                            }
911                            if (error) what_error = "ACI";
912                        }
913
914                        if (!error && (match->append || match->write)) {
915                            char *field = NULp;
916                            char *tag   = NULp;
917
918                            {
919                                bool   err_flag;
920                                string expanded_field = expandSetVariables(variables, string(match->append ? match->append : match->write), err_flag, ifo);
921                                if (err_flag) error   = GB_await_error();
922                                else   field          = GBS_string_2_key(expanded_field.c_str());
923                                if (error) what_error = match->append ? "APPEND" : "WRITE";
924                            }
925
926                            if (!error && match->mtag) {
927                                bool   err_flag;
928                                string expanded_tag = expandSetVariables(variables, string(match->mtag), err_flag, ifo);
929                                if (err_flag) error = GB_await_error();
930                                else   tag          = GBS_string_2_key(expanded_tag.c_str());
931                                if (error) what_error = "TAG";
932                            }
933
934                            if (!error) {
935                                write_entry(gb_import_main, gb_species, field, s, tag, bool(match->append), match->type);
936                            }
937                            free(tag);
938                            free(field);
939                        }
940
941                        if (!error && match->setvar) variables.set(match->setvar[0], s);
942                        free(dele);
943                    }
944
945                    if (error) {
946                        error = GBS_global_string("'%s'\nin %s of MATCH (defined at #%s)", error, what_error, match->defined_at);
947                    }
948                }
949            }
950
951            if (error) {
952                return GBS_global_string("%s\nwhile parsing line #%i of species #%i", error, line, rel_counter);
953            }
954
955            if (GBS_string_matches(p, ifo->sequencestart, GB_MIND_CASE)) goto read_sequence;
956
957            p = read_line(ifo->tab, ifo->sequencestart, ifo->sequenceend);
958            if (!p) break;
959        }
960        return GB_export_errorf("No Start of Sequence found (%i lines read)", max_line);
961
962      read_sequence :
963        {
964            char *sequence;
965            {
966                GBS_strstruct buf(5000);
967                for (int linecnt = 0; ; linecnt++) {
968                    if (linecnt || !ifo->read_this_sequence_line_too) {
969                        p = read_line(0, ifo->sequencestart, ifo->sequenceend);
970                    }
971                    if (!p) break;
972                    if (ifo->sequenceend && GBS_string_matches(p, ifo->sequenceend, GB_MIND_CASE)) break;
973                    if (strlen(p) <= ifo->sequencecolumn) continue;
974                    buf.cat(p+ifo->sequencecolumn);
975                }
976                sequence = buf.release();
977            }
978
979            GBDATA *gb_data = GBT_create_sequence_data(gb_species, ali_name, "data", GB_STRING, security_write);
980            if (ifo->sequencesrt) {
981                char *h = GBS_string_eval_in_env(sequence, ifo->sequencesrt, callEnv);
982                if (!h) return GB_await_error();
983                freeset(sequence, h);
984            }
985
986            if (ifo->sequenceaci) {
987                char *h = GB_command_interpreter_in_env(sequence, ifo->sequenceaci, callEnv);
988                free(sequence);
989                if (!h) return GB_await_error();
990                sequence = h;
991            }
992
993            GB_write_string(gb_data, sequence);
994
995            GBDATA *gb_acc = GB_search(gb_species, "acc", GB_FIND);
996            if (!gb_acc && ifo->autocreateacc) {
997                char buf[100];
998                long id = GBS_checksum(sequence, 1, ".-");
999                sprintf(buf, "ARB_%lX", id);
1000                gb_acc = GB_search(gb_species, "acc", GB_STRING);
1001                GB_write_string(gb_acc, buf);
1002            }
1003            free(sequence);
1004        }
1005
1006        if (ruleset.isSet()) {
1007            awti_assert(!GB_have_error());
1008            ItemClonedByRuleSet clone(gb_species, CLONE_ITEM_SPECIES, ruleset, REPLACE_ITEM_BY_CLONE, NULp, NULp);
1009            if (clone.has_error()) return clone.get_error();
1010            // continue with items clone.
1011        }
1012
1013        while (1) { // go to the start of an species
1014            if (!p || !ifo->begin || GBS_string_matches(p, ifo->begin, GB_MIND_CASE)) break;
1015            p = read_line(0, ifo->sequencestart, ifo->sequenceend);
1016        }
1017    }
1018    return NULp;
1019}
1020
1021inline bool contains_wildcards(const char *mask) { return strpbrk(mask, "?*") != NULp; }
1022
1023static GB_ERROR no_files_found_error(const char *mask) {
1024    GB_ERROR error = NULp;
1025
1026    if      (GB_have_error())          error = GB_await_error();
1027    else if (!mask[0])                 error = "No file selected to import"; // empty filespec
1028    else if (contains_wildcards(mask)) error = GBS_global_string("No file matched '%s'", mask);
1029    else                               error = GBS_global_string("No such file ('%s')", mask);
1030
1031    return error;
1032}
1033
1034GB_ERROR ArbImporter::load_format(AW_root *awr) {
1035    // load import filter(s)
1036    GB_ERROR  error = NULp;
1037    char     *file  = awr->awar(AWAR_IMPORT_FORMATNAME)->read_string();
1038
1039    if (!file[0]) {
1040        error = "no import filter selected";
1041    }
1042    else {
1043        error = read_format(file);
1044        if (!error && ifo->new_format) {
1045            ifo2 = ifo;
1046            ifo  = NULp;
1047
1048            error = read_format(ifo2->new_format);
1049            if (!error) {
1050                if (ifo->new_format) {
1051                    error = GBS_global_string("in line %zi of import filter '%s':\n"
1052                                              "Only one level of form nesting (NEW_FORMAT) allowed",
1053                                              ifo->new_format_lineno, name_only(ifo2->new_format));
1054                }
1055            }
1056            if (error) {
1057                error = GBS_global_string("in format used in line %zi of '%s':\n%s",
1058                                          ifo2->new_format_lineno, name_only(file), error);
1059            }
1060        }
1061    }
1062    free(file);
1063    return error;
1064}
1065
1066GB_ERROR ArbImporter::import_data(AW_root *awr, const char *mask, bool keep_found_IDs) {
1067    // Import sequences into new or existing database
1068    // if 'keep_found_IDs' is true => do not ask (mode used by format-tester)
1069
1070    awti_assert(!GB_have_error());
1071
1072    bool     is_genom_db             = false;
1073    bool     delete_db_type_if_error = false; // delete db type (genome/normal) in case of error ?
1074    GB_ERROR error                   = NULp;
1075
1076    {
1077        bool           read_genom_db = wants_import_genome(awr);
1078        GB_transaction ta(gb_import_main);
1079
1080        delete_db_type_if_error = !GB_entry(gb_import_main, GENOM_DB_TYPE);
1081        is_genom_db             = GEN_is_genome_db(gb_import_main, read_genom_db);
1082
1083        if (read_genom_db!=is_genom_db) {
1084            if (is_genom_db) {
1085                error = "You can only import whole genom sequences into a genom database.";
1086            }
1087            else {
1088                error = "You can't import whole genom sequences into a non-genom ARB database.";
1089            }
1090            awti_assert(!GB_have_error());
1091            return error;
1092        }
1093    }
1094
1095    {
1096        GB_securityLevel raised(gb_import_main, 6);
1097
1098        GB_begin_transaction(gb_import_main); // first transaction start
1099        char *ali_name;
1100        {
1101            char *ali = awr->awar(AWAR_IMPORT_ALI)->read_string();
1102            ali_name = GBS_string_eval(ali, SRT_AUTOCORRECT_ALINAME);
1103            free(ali);
1104        }
1105
1106        error = GBT_check_alignment_name(ali_name);
1107
1108        int ali_protection = awr->awar(AWAR_IMPORT_ALI_PROTECTION)->read_int();
1109        if (!error) {
1110            char *ali_type;
1111            ali_type = awr->awar(AWAR_IMPORT_ALI_TYPE)->read_string();
1112
1113            if (is_genom_db && strcmp(ali_type, "dna")!=0) {
1114                error = "You must set the alignment type to dna when importing genom sequences.";
1115            }
1116            else {
1117                if (!GBT_create_alignment(gb_import_main, ali_name, 2000, 0, ali_protection, ali_type, "while importing data")) {
1118                    error = GB_await_error();
1119                }
1120            }
1121            free(ali_type);
1122        }
1123
1124        bool ask_generate_names = true;
1125
1126        if (!error) {
1127            if (is_genom_db) {
1128                // import genome flatfile into ARB-genome database:
1129
1130                StrArray fnames;
1131                GBS_read_dir(fnames, mask, NULp);
1132                if (fnames.empty()) error = no_files_found_error(mask);
1133
1134                if (!error) {
1135                    const char *fts = awr->awar(AWAR_IMPORT_FTS)->read_char_pntr();
1136                    if (fts[0]) error = "field transfer sets do not work with genome flatfile importer";
1137                }
1138
1139                if (!error) {
1140                    int successfull_imports = 0;
1141                    int failed_imports      = 0;
1142
1143                    long count;
1144                    for (count = 0; fnames[count]; ++count) ; // count filenames
1145
1146                    GBDATA *gb_species_data = GBT_get_species_data(gb_import_main);
1147                    ImportSession import_session(gb_species_data, count*10);
1148
1149                    // close the above transaction and do each importfile in separate transaction
1150                    // to avoid that all imports are undone by transaction abort happening in case of error
1151                    GB_commit_transaction(gb_import_main);
1152
1153                    arb_progress progress("Reading input files", count);
1154
1155                    for (long curr = 0; !error && fnames[curr]; ++curr) {
1156                        GB_ERROR error_this_file =  NULp;
1157
1158                        GB_begin_transaction(gb_import_main);
1159                        {
1160                            const char *lslash = strrchr(fnames[curr], '/');
1161                            progress.subtitle(GBS_global_string("%li/%li: %s", curr+1, count, lslash ? lslash+1 : fnames[curr]));
1162                        }
1163
1164#if defined(DEBUG)
1165                        fprintf(stderr, "import of '%s'\n", fnames[curr]);
1166#endif // DEBUG
1167                        error_this_file = GI_importGenomeFile(import_session, fnames[curr], ali_name);
1168
1169                        if (!error_this_file) {
1170                            GB_commit_transaction(gb_import_main);
1171                            successfull_imports++;
1172                            delete_db_type_if_error = false;
1173                        }
1174                        else { // error occurred during import
1175                            error_this_file = GBS_global_string("'%s' not imported\nReason: %s", fnames[curr], error_this_file);
1176                            GB_warningf("Import error: %s", error_this_file);
1177                            GB_abort_transaction(gb_import_main);
1178                            failed_imports++;
1179                        }
1180
1181                        progress.inc_and_check_user_abort(error);
1182                    }
1183
1184                    if (!successfull_imports) {
1185                        error = "Nothing has been imported";
1186                    }
1187                    else {
1188                        GB_warningf("%i of %i files were imported with success", successfull_imports, (successfull_imports+failed_imports));
1189                    }
1190
1191                    // now open another transaction to "undo" the transaction close above
1192                    GB_begin_transaction(gb_import_main);
1193                }
1194            }
1195            else {
1196                // import to non-genome ARB-db:
1197
1198                error = load_format(awr); // load import filter
1199
1200                RuleSetPtr ruleset;
1201                if (!error) {
1202                    const char *fts      = awr->awar(AWAR_IMPORT_FTS)->read_char_pntr();
1203                    const char *full_fts = XFER_getFullFTS(fts);
1204
1205                    if (full_fts[0]) { // any ruleset selected?
1206                        ErrorOrRuleSetPtr loaded = RuleSet::loadFrom(full_fts);
1207
1208                        if (loaded.hasError()) {
1209                            ARB_ERROR lerror = loaded.getError();
1210                            error            = lerror.deliver();
1211                        }
1212                        else {
1213                            ruleset = loaded.getValue();
1214                        }
1215                    }
1216                }
1217
1218                if (!error) {
1219                    GBS_read_dir(filenames, mask, NULp);
1220                    if (filenames.empty()) error = no_files_found_error(mask);
1221                }
1222
1223                if (!error) {
1224                    arb_progress progress("Reading input files");
1225
1226                    error = read_data(ali_name, ali_protection, ruleset);
1227                    if (error) {
1228                        error = GBS_global_string("Error: %s\nwhile reading file %s", error, filenames[current_file_idx-1]);
1229                    }
1230                    else {
1231                        if (ifo->noautonames || (ifo2 && ifo2->noautonames)) {
1232                            ask_generate_names = false;
1233                        }
1234                        else {
1235                            ask_generate_names = true;
1236                        }
1237                    }
1238                }
1239
1240                unload_format();
1241                if (in) { fclose(in); in = NULp; }
1242
1243                filenames.erase();
1244                current_file_idx = 0;
1245            }
1246        }
1247        free(ali_name);
1248
1249        if (error) {
1250            GB_abort_transaction(gb_import_main);
1251
1252            if (delete_db_type_if_error) {
1253                // delete db type, if it was initialized above
1254                // (avoids 'can't import'-error, if file-type (genome-file/species-file) is changed after a failed try)
1255                GB_transaction  ta(gb_import_main);
1256                GBDATA         *db_type = GB_entry(gb_import_main, GENOM_DB_TYPE);
1257                if (db_type) GB_delete(db_type);
1258            }
1259        }
1260        else {
1261            arb_progress progress("Checking and Scanning database", long(2+ask_generate_names)); // 2 or 3 passes
1262            progress.subtitle("Pass 1: Check entries");
1263
1264            // scan for hidden/unknown fields :
1265            species_field_selection_list_rescan(gb_import_main, RESCAN_REFRESH);
1266            if (is_genom_db) gene_field_selection_list_rescan(gb_import_main, RESCAN_REFRESH);
1267
1268            GBT_mark_all(gb_import_main, 1);
1269            progress.inc();
1270            progress.subtitle("Pass 2: Check sequence lengths");
1271            GBT_check_data(gb_import_main, NULp);
1272
1273            GB_commit_transaction(gb_import_main);
1274            progress.inc();
1275
1276            if (ask_generate_names && !keep_found_IDs) {
1277                if (aw_question("generate_short_names",
1278                                "It's recommended to generate unique species identifiers now.\n",
1279                                "Generate unique species IDs,Use found IDs") == 0)
1280                {
1281                    progress.subtitle("Pass 3: Generate unique species IDs");
1282                    error = AW_select_nameserver(gb_import_main, gb_main_4_nameserver);
1283                    if (!error) {
1284                        bool isDuplicatesWarning;
1285                        error = AWTC_pars_names(gb_import_main, &isDuplicatesWarning);
1286                        if (error && isDuplicatesWarning) {
1287                            aw_message(error); // warn, but ..
1288                            error = NULp;      // .. do not handle as error (otherwise import will abort)
1289                        }
1290                    }
1291                }
1292                progress.inc();
1293            }
1294        }
1295    }
1296
1297    // gb_main_4_nameserver = NULp; // this was only needed once. may interfere with import-tester
1298
1299    awti_assert(!GB_have_error());
1300    return error;
1301}
1302
1303void ArbImporter::import_and_continueOnSuccess(AW_window *aww) {
1304    AW_root  *awr   = aww->get_root();
1305    char     *mask  = awr->awar(AWAR_IMPORT_FILENAME)->read_string();
1306    GB_ERROR  error = import_data(awr, mask, false);
1307    if (error) {
1308        aw_message(error);
1309    }
1310    else {
1311        aww->hide(); // import window stays open in case of error
1312        after_import_cb(awr);
1313    }
1314    free(mask);
1315}
1316
1317inline bool is_dynamic(const char *field) { return strchr(field, '$') != NULp; } // related to 'expandSetVariables'
1318
1319void ArbImporter::detectAvailableFields(StrArray& fields, FieldsToScan whatToScan) {
1320    if (whatToScan & SCAN_INPUT_FIELDS) {
1321        GB_ERROR error = load_format(AW_root::SINGLETON);
1322        if (error) {
1323            fields.put(GBS_global_string_copy("<%s>", error));
1324        }
1325        else {
1326            for (const import_format *iform = ifo; iform; iform = (iform == ifo) ? ifo2 : NULp) {
1327                for (const import_match *matcher = iform->match; matcher; matcher = matcher->next) {
1328                    if (matcher->append && !is_dynamic(matcher->append)) fields.put(ARB_strdup(matcher->append));
1329                    if (matcher->write  && !is_dynamic(matcher->write))  fields.put(ARB_strdup(matcher->write));
1330                }
1331            }
1332        }
1333        unload_format();
1334    }
1335
1336    if (whatToScan & SCAN_OUTPUT_FIELDS) {
1337        if (gb_main_4_nameserver) {
1338            // collect fields existing in target DB:
1339            collectKeysRegisteredInDatabase(fields, gb_main_4_nameserver, SPECIES_get_selector(), true, false);
1340        }
1341        else {
1342            fields.put(ARB_strdup("<no target database>"));
1343        }
1344    }
1345}
1346
1347class AliNameAndType {
1348    string name_, type_;
1349public:
1350    AliNameAndType(const char *ali_name, const char *ali_type) : name_(ali_name), type_(ali_type) {}
1351
1352    const char *name() const { return name_.c_str(); }
1353    const char *type() const { return type_.c_str(); }
1354};
1355
1356static AliNameAndType last_ali("ali_new", "rna"); // last selected ali for plain import (aka non-flatfile import)
1357
1358
1359void AWTI_import_set_ali_and_type(AW_root *awr, const char *ali_name, const char *ali_type, GBDATA *gbmain) {
1360    bool           switching_to_GENOM_ALIGNMENT = strcmp(ali_name, GENOM_ALIGNMENT) == 0;
1361    static GBDATA *last_valid_gbmain            = NULp;
1362
1363    if (gbmain) last_valid_gbmain = gbmain;
1364
1365    AW_awar *awar_name = awr->awar(AWAR_IMPORT_ALI);
1366    AW_awar *awar_type = awr->awar(AWAR_IMPORT_ALI_TYPE);
1367
1368    if (switching_to_GENOM_ALIGNMENT) {
1369        // read and store current settings
1370        char *curr_ali_name = awar_name->read_string();
1371        char *curr_ali_type = awar_type->read_string();
1372
1373        last_ali = AliNameAndType(curr_ali_name, curr_ali_type);
1374
1375        free(curr_ali_name);
1376        free(curr_ali_type);
1377    }
1378
1379    awar_name->write_string(ali_name);
1380    awar_type->write_string(ali_type);
1381
1382    if (last_valid_gbmain) { // detect default write protection for alignment
1383        GB_transaction ta(last_valid_gbmain);
1384        GBDATA *gb_ali            = GBT_get_alignment(last_valid_gbmain, ali_name);
1385        int     protection_to_use = 4;         // default protection
1386
1387        if (gb_ali) {
1388            GBDATA *gb_write_security = GB_entry(gb_ali, "alignment_write_security");
1389            if (gb_write_security) {
1390                protection_to_use = GB_read_int(gb_write_security);
1391            }
1392        }
1393        else {
1394            GB_clear_error();
1395        }
1396        awr->awar(AWAR_IMPORT_ALI_PROTECTION)->write_int(protection_to_use);
1397    }
1398}
1399
1400static void genom_flag_changed(AW_root *awr) {
1401    if (wants_import_genome(awr)) {
1402        AWTI_import_set_ali_and_type(awr, GENOM_ALIGNMENT, "dna", NULp);
1403        awr->awar(AWAR_IMPORT_FORMATFILTER)->write_string(".fit"); // *hack* to hide normal import filters
1404    }
1405    else {
1406        AWTI_import_set_ali_and_type(awr, last_ali.name(), last_ali.type(), NULp);
1407        awr->awar(AWAR_IMPORT_FORMATFILTER)->write_string(".ift");
1408    }
1409}
1410
1411// --------------------------------------------------------------------------------
1412
1413static ArbImporter *importer = NULp;
1414
1415void AWTI_cleanup_importer() {
1416    if (importer) {
1417#if defined(DEBUG)
1418        AWT_browser_forget_db(importer->peekImportDB());
1419#endif
1420        delete importer; // closes the import DB if it still is owned by the 'importer'
1421        importer = NULp;
1422    }
1423}
1424
1425static void import_window_close_cb(AW_window *aww, bool *doExit) {
1426    if (importer) {
1427        AWTI_cleanup_importer();
1428        if (*doExit) exit(EXIT_SUCCESS);
1429        else AW_POPDOWN(aww);
1430    }
1431    else {
1432        AW_POPDOWN(aww);
1433    }
1434}
1435
1436static void import_and_continue_cb(AW_window *aww) { importer->import_and_continueOnSuccess(aww); }
1437static void detect_input_format_cb(AW_window *aww) {
1438    if (wants_import_genome(aww->get_root())) {
1439        aw_message("Only works together with 'Import selected format'");
1440    }
1441    else {
1442        importer->detect_format(aww->get_root());
1443    }
1444}
1445
1446static void update_format_description(const char *ift) {
1447    const char *description = NULp;
1448
1449    if (ift && !ift[0]) {
1450        description = "";
1451    }
1452    else {
1453        GB_ERROR err = importer->read_format(ift);
1454
1455        if (err) {
1456            description = GBS_global_string("Error reading format:\n%s", err);
1457        }
1458        else {
1459            const import_format *ifo = importer->peek_format();
1460
1461            if      (!ifo || !ift[0])   description = "<no format selected>";
1462            else if (!ifo->description) description = "<no description>";
1463            else                        description = ifo->description;
1464        }
1465    }
1466    AW_root::SINGLETON->awar(AWAR_IMPORT_FORMAT_DESC)->write_string(description);
1467}
1468
1469void AWTI_set_importDB_pointer(GBDATA*& dbPtr) {
1470    awti_assert(importer);
1471    GBDATA *gb_imain = importer->peekImportDB();
1472    awti_assert(gb_imain);
1473    awti_assert(!dbPtr || dbPtr == gb_imain);
1474    dbPtr = gb_imain;
1475}
1476GBDATA *AWTI_acquire_imported_DB_and_cleanup_importer() {
1477    awti_assert(importer && importer->peekImportDB());
1478    GBDATA *gb_imported_main = importer->takeImportDB();
1479    AWTI_cleanup_importer();
1480    return gb_imported_main;
1481}
1482static void create_import_awars(AW_root *awr, const char *def_importname) {
1483    {
1484        GBS_strstruct path(500);
1485        path.cat(GB_path_in_arbprop("filter"));
1486        path.put(':');
1487        path.cat(GB_path_in_ARBLIB("import"));
1488
1489        AW_create_fileselection_awars(awr, AWAR_IMPORT_FILEBASE,   ".",             "",     def_importname);
1490        AW_create_fileselection_awars(awr, AWAR_IMPORT_FORMATBASE, path.get_data(), ".ift", "*");
1491    }
1492
1493    awr->awar_string(AWAR_IMPORT_FORMAT_DESC,    "<undefined>");
1494    awr->awar_string(AWAR_IMPORT_ALI,            "<undefined>"); // these defaults are never used
1495    awr->awar_string(AWAR_IMPORT_ALI_TYPE,       "<undefined>"); // they are overwritten by AWTI_import_set_ali_and_type
1496    awr->awar_int   (AWAR_IMPORT_ALI_PROTECTION, 0);             // which is called via genom_flag_changed() below
1497
1498    awr->awar_string(AWAR_IMPORT_FTS, "");
1499
1500    awr->awar_int(AWAR_IMPORT_GENOM_DB, IMP_PLAIN_SEQUENCE);
1501    awr->awar_int(AWAR_IMPORT_AUTOCONF, 1);
1502
1503    awr->awar(AWAR_IMPORT_GENOM_DB)->add_callback(genom_flag_changed);
1504    genom_flag_changed(awr);
1505}
1506
1507class ImportFieldScanner : public AvailableFieldScanner {
1508public:
1509    ImportFieldScanner() {}
1510    void scanFields(StrArray& fields, FieldsToScan whatToScan) const OVERRIDE {
1511        if (importer) {
1512            importer->detectAvailableFields(fields, whatToScan);
1513        }
1514        else {
1515            fields.put(ARB_strdup("<no importer?!>")); // should never happen
1516        }
1517    }
1518};
1519
1520static ImportFieldScanner fieldScanner;
1521
1522void AWTI_open_import_window(AW_root *awr, const char *def_importname, bool do_exit, GBDATA *gb_main, const RootCallback& after_import_cb) {
1523    static AW_window_simple *aws = NULp;
1524
1525    if (!importer) {
1526        importer = new ArbImporter(after_import_cb);
1527
1528#if defined(DEBUG)
1529        AWT_announce_db_to_browser(importer->peekImportDB(), "New database (import)");
1530#endif // DEBUG
1531
1532        importer->set_db_4_nameserver(gb_main);
1533        if (!gb_main) {
1534            // control macros via temporary import DB (if no main DB available)
1535            GB_ERROR error = configure_macro_recording(awr, "ARB_IMPORT", importer->peekImportDB());
1536            aw_message_if(error);
1537        }
1538        else {
1539            awti_assert(got_macro_ability(awr));
1540        }
1541    }
1542
1543    static bool doExit;
1544    doExit = do_exit; // change/set behavior of CLOSE button
1545
1546    if (aws) {
1547        if (def_importname) {
1548            awr->awar(AWAR_IMPORT_FILENAME)->write_string(def_importname);
1549        }
1550    }
1551    else {
1552        create_import_awars(awr, def_importname);
1553
1554        aws = new AW_window_simple;
1555
1556        aws->init(awr, "ARB_IMPORT", "ARB IMPORT");
1557        aws->load_xfig("awt/import_db.fig");
1558
1559        aws->at("close");
1560        aws->callback(makeWindowCallback(import_window_close_cb, &doExit));
1561        aws->create_button("CLOSE", "CLOSE", "C");
1562
1563        aws->at("help");
1564        aws->callback(makeHelpCallback("arb_import.hlp"));
1565        aws->create_button("HELP", "HELP", "H");
1566
1567        AW_create_fileselection(aws, AWAR_IMPORT_FILEBASE,   "imp_", "PWD",     ANY_DIR,    true);  // select import filename
1568        AW_create_fileselection(aws, AWAR_IMPORT_FORMATBASE, "",     "ARBHOME", MULTI_DIRS, false); // select import filter
1569
1570        aws->at("auto");
1571        aws->callback(detect_input_format_cb);
1572        aws->create_autosize_button("AUTO_DETECT", "AUTO DETECT", "A");
1573
1574        aws->at("ali");
1575        aws->create_input_field(AWAR_IMPORT_ALI, 4);
1576
1577        aws->at("type");
1578        aws->create_option_menu(AWAR_IMPORT_ALI_TYPE);
1579        aws->insert_option        ("dna",     "d", "dna");
1580        aws->insert_default_option("rna",     "r", "rna");
1581        aws->insert_option        ("protein", "p", "ami");
1582        aws->update_option_menu();
1583
1584        aws->at("protect");
1585        aws->create_option_menu(AWAR_IMPORT_ALI_PROTECTION);
1586        aws->insert_option("0", "0", 0);
1587        aws->insert_option("1", "1", 1);
1588        aws->insert_option("2", "2", 2);
1589        aws->insert_option("3", "3", 3);
1590        aws->insert_default_option("4", "4", 4);
1591        aws->insert_option("5", "5", 5);
1592        aws->insert_option("6", "6", 6);
1593        aws->update_option_menu();
1594
1595        aws->at("genom");
1596        aws->create_toggle_field(AWAR_IMPORT_GENOM_DB);
1597        aws->sens_mask(AWM_EXP);
1598        aws->insert_toggle("Import genome data in EMBL, GenBank or DDBJ format", "e", IMP_GENOME_FLATFILE);
1599        aws->sens_mask(AWM_ALL);
1600        aws->insert_toggle("Import selected format", "f", IMP_PLAIN_SEQUENCE);
1601        aws->update_toggle_field();
1602
1603        aws->at("autoconf");
1604        aws->create_toggle(AWAR_IMPORT_AUTOCONF);
1605
1606        aws->at("fts");
1607        aws->callback(makeWindowCallback(XFER_select_RuleSet, AWAR_IMPORT_FTS, static_cast<AvailableFieldScanner*>(&fieldScanner)));
1608        aws->create_button("SELECT_FTS", AWAR_IMPORT_FTS);
1609
1610        aws->at("desc");
1611        aws->create_text_field(AWAR_IMPORT_FORMAT_DESC);
1612
1613        aws->at("go");
1614        aws->callback(import_and_continue_cb);
1615        aws->highlight();
1616        aws->create_button("GO", "GO", "G", "+");
1617
1618        aws->at("test");
1619        aws->callback(AWTI_activate_import_test_window);
1620        aws->create_button("TEST", "Test", "T");
1621
1622        static FileWatch fwatch(AWAR_IMPORT_FORMATNAME, makeFileChangedCallback(update_format_description));
1623        awr->awar(AWAR_IMPORT_FORMATNAME)->add_callback(makeRootCallback(XFER_refresh_available_fields, static_cast<AvailableFieldScanner*>(&fieldScanner), SCAN_INPUT_FIELDS));
1624    }
1625    aws->activate();
1626}
1627
1628// --------------------------------------------------------------------------------
1629
1630#ifdef UNIT_TESTS
1631#ifndef TEST_UNIT_H
1632#include <test_unit.h>
1633#endif
1634
1635static void after_import_test_cb(AW_root*) {}
1636
1637void TEST_import_filters_loadable() {
1638    GB_shell    shell;
1639    ArbImporter testImporter(makeRootCallback(after_import_test_cb));
1640
1641    // Test that delivered filters are loadable w/o error:
1642    StrArray impFilt;
1643    GBS_read_dir(impFilt, GB_path_in_ARBLIB("import"), "/\\.ift$/"); // all filters in ../lib/import
1644    removeHiddenFiles(impFilt);
1645    TEST_EXPECT_EQUAL(impFilt.size(), 9);                            // amount of import filters
1646
1647    for (int i = 0; impFilt[i]; ++i) {
1648        const char *name = strrchr(impFilt[i], '/')+1;
1649        TEST_ANNOTATE(name);
1650        TEST_EXPECT_NO_ERROR(testImporter.read_format(impFilt[i]));
1651
1652        if (strcmp(name, "fasta_wgap.ift") == 0) {
1653            TEST_EXPECT_EQUAL(testImporter.peek_format()->description,
1654                              "Imports sequences in FASTA format (does not remove gaps)\n"
1655                              "Expected header: '>id fullname|...'");
1656        }
1657    }
1658}
1659
1660#endif // UNIT_TESTS
1661
1662// --------------------------------------------------------------------------------
1663
Note: See TracBrowser for help on using the repository browser.