source: tags/ms_r17q3/SL/SEQIO/seq_export.cxx

Last change on this file was 16564, checked in by westram, 7 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.3 KB
Line 
1// ============================================================= //
2//                                                               //
3//   File      : seq_export.cxx                                  //
4//   Purpose   :                                                 //
5//                                                               //
6//   Institute of Microbiology (Technical University Munich)     //
7//   http://www.arb-home.de/                                     //
8//                                                               //
9// ============================================================= //
10
11#include "seqio.hxx"
12
13#include <AP_filter.hxx>
14#include <arbdbt.h>
15#include <gb_aci.h>
16
17#include <arb_strarray.h>
18#include <arb_file.h>
19#include <arb_diff.h>
20#include <arb_progress.h>
21
22#include <xml.hxx>
23
24#include <unistd.h>
25
26#define sio_assert(cond) arb_assert(cond)
27
28using           std::string;
29using namespace SEQIO;
30
31// ---------------------------------
32//      internal export commands
33
34enum EXPORT_CMD {
35    // real formats
36    EXPORT_XML,
37
38    EXPORT_INVALID,
39    EXPORT_USING_FORM,        // default mode (has to be last entry in enum)
40};
41
42static const char *internal_export_commands[] = {
43    "xml_write",
44    NULL
45};
46
47static EXPORT_CMD check_internal(const char *command) {
48    EXPORT_CMD cmd = EXPORT_INVALID;
49    for (int i = 0; internal_export_commands[i]; ++i) {
50        if (strcmp(command, internal_export_commands[i]) == 0) {
51            cmd = static_cast<EXPORT_CMD>(i);
52        }
53    }
54    return cmd;
55}
56
57// ----------------------
58//      export_format
59
60struct export_format : virtual Noncopyable {
61    char *system;
62    char *new_format;
63    char *suffix;
64    char *description; // (multiline) description of filter
65    char *form;        // transformed export expression (part behind 'BEGIN')
66
67    EXPORT_CMD export_mode;
68
69    export_format()
70        : system(NULL),
71          new_format(NULL),
72          suffix(NULL),
73          description(NULL),
74          form(NULL),
75          export_mode(EXPORT_XML)
76    {}
77    ~export_format() {
78        free(system);
79        free(new_format);
80        free(suffix);
81        free(description);
82        free(form);
83    }
84};
85
86static GB_ERROR read_export_format(export_format *efo, const char *file, bool load_complete_form) {
87    GB_ERROR error = 0;
88
89    if (!file || !file[0]) {
90        error = "No export format selected";
91    }
92    else {
93        char *fullfile = 0;
94        if (GB_is_regularfile(file)) { // prefer files that are completely specified (full/rel path)
95            fullfile = strdup(GB_canonical_path(file));
96        }
97        else {
98            fullfile = nulldup(GB_path_in_ARBHOME(file)); // fallback to ARBHOME-relative specification
99        }
100
101        FILE *in = fopen(fullfile, "r");
102
103        if (!in) error = GB_IO_error("reading export form", fullfile);
104        else {
105            efo->export_mode = EXPORT_USING_FORM; // default mode
106            {
107                bool    seen_BEGIN = false;
108                char   *s1, *s2;
109                size_t  linenumber = 0;
110
111                while (!error && !seen_BEGIN && read_string_pair(in, s1, s2, linenumber)) {
112                    if      (!strcmp(s1, "SYSTEM"))      { reassign(efo->system,     s2); }
113                    else if (!strcmp(s1, "PRE_FORMAT"))  { reassign(efo->new_format, s2); }
114                    else if (!strcmp(s1, "SUFFIX"))      { reassign(efo->suffix,     s2); }
115                    else if (!strcmp(s1, "DESCRIPTION")) { appendTo(efo->description, '\n', s2); }
116                    else if (!strcmp(s1, "INTERNAL")) {
117                        efo->export_mode = check_internal(s2);
118                        if (efo->export_mode == EXPORT_INVALID) {
119                            error = GBS_global_string("Unknown INTERNAL command '%s'", s2);
120                        }
121                    }
122                    else if (!strcmp(s1, "BEGIN")) {
123                        if (efo->export_mode != EXPORT_USING_FORM) {
124                            error = "'BEGIN' not allowed when 'INTERNAL' is used";
125                        }
126                        else {
127                            seen_BEGIN = true;
128                        }
129                    }
130                    else {
131                        error = GBS_global_string("Unknown command '%s'", s1);
132                    }
133
134                    // add error location
135                    if (error) error = GBS_global_string("%s in line #%zu", error, linenumber);
136
137                    free(s2);
138                    free(s1);
139                }
140            }
141
142            if (!error && load_complete_form && efo->export_mode == EXPORT_USING_FORM) {
143                // now 'in' points to line behind 'BEGIN'
144                char *form = GB_read_fp(in); // read rest of file
145
146                // Join lines that end with \ with next line.
147                // Replace ' = ' and ':' by '\=' and '\:'
148                efo->form  = GBS_string_eval(form, "\\\\\n=:\\==\\\\\\=:*=\\*\\=*1:\\:=\\\\\\:");
149                if (!efo->form) error = GB_failedTo_error("evaluate part below 'BEGIN'", NULL, GB_await_error());
150                free(form);
151            }
152
153            // some checks for incompatible commands
154            if (!error) {
155                if      (efo->system && !efo->new_format) error = "Missing 'PRE_FORMAT' (needed by 'SYSTEM')";
156                else if (efo->new_format && !efo->system) error = "Missing 'SYSTEM' (needed by 'PRE_FORMAT')";
157                else if (efo->export_mode != EXPORT_USING_FORM) {
158                    if (efo->system)     error = "'SYSTEM' is not allowed together with 'INTERNAL'";
159                    if (efo->new_format) error = "'PRE_FORMAT' is not allowed together with 'INTERNAL'";
160                }
161            }
162
163            error = GB_failedTo_error("read export format", fullfile, error);
164            fclose(in);
165        }
166        free(fullfile);
167    }
168
169    return error;
170}
171
172// ----------------------------------------
173// export sequence helper class
174
175class SpeciesSelector : virtual Noncopyable {
176    ExportWhich  which;
177    const char  *one_species;
178
179public:
180    SpeciesSelector(ExportWhich which_, const char *one_species_) :
181        which(which_),
182        one_species(one_species_)
183    {}
184    GBDATA *select_first(GBDATA *gb_main) const {
185        GBDATA *gb_species = NULL;
186        switch (which) {
187            case EBF_ALL:    gb_species = GBT_first_species(gb_main);             break;
188            case EBF_MARKED: gb_species = GBT_first_marked_species(gb_main);      break;
189            case EBF_ONE:    gb_species = GBT_find_species(gb_main, one_species); break;
190        }
191        return gb_species;
192    }
193    GBDATA *select_next(GBDATA *gb_previous) const {
194        GBDATA *gb_species = NULL;
195        switch (which) {
196            case EBF_ALL:    gb_species = GBT_next_species(gb_previous);        break;
197            case EBF_MARKED: gb_species = GBT_next_marked_species(gb_previous); break;
198            case EBF_ONE:    break;
199        }
200        return gb_species;
201    }
202};
203
204class export_sequence_data : virtual Noncopyable { // @@@ simplify using FilteredExport?
205    GBDATA *last_species_read;
206    char   *seq;
207    size_t  len;
208    char   *error;
209
210    GBDATA *gb_main;
211    char   *ali;
212
213    SpeciesSelector whichSpecies;
214
215    size_t     species_count;
216    AP_filter *filter;
217    bool       cut_stop_codon;
218    int        compress;           // 0 = no;1 = vertical gaps; 2 = all gaps;
219
220    long    max_ali_len;                            // length of alignment
221    size_t *export_column;                          // list of exported seq data positions
222    size_t  columns;                                // how many columns get exported
223
224    GBDATA *single_species;     // if != NULL -> first/next only return that species (used to export to multiple files)
225
226public:
227
228    export_sequence_data(GBDATA *Gb_Main, ExportWhich which, const char *one_species, AP_filter* Filter, bool CutStopCodon, int Compress) :
229        last_species_read(0),
230        seq(0),
231        len(0),
232        error(0),
233        gb_main(Gb_Main),
234        whichSpecies(which, one_species),
235        species_count(size_t(-1)),
236        filter(Filter),
237        cut_stop_codon(CutStopCodon),
238        compress(Compress),
239        export_column(0),
240        columns(0),
241        single_species(0)
242    {
243        ali         = GBT_get_default_alignment(gb_main);
244        max_ali_len = GBT_get_alignment_len(gb_main, ali);
245
246        if (cut_stop_codon) {
247            GB_alignment_type ali_type = GBT_get_alignment_type(gb_main, ali);
248            if (ali_type !=  GB_AT_AA) {
249                GB_warning("Cutting stop codon makes no sense - ignored");
250                cut_stop_codon = false;
251            }
252        }
253        sio_assert(filter);
254
255        if (max_ali_len>=0 && filter->get_length() < size_t(max_ali_len)) {
256            GB_warningf("Warning: Your filter is shorter than the alignment (%zu<%li)",
257                        filter->get_length(), max_ali_len);
258            max_ali_len = filter->get_length();
259        }
260    }
261
262    ~export_sequence_data() {
263        delete [] export_column;
264        delete [] seq;
265        free(error);
266        free(ali);
267    }
268
269    const char *getAlignment() const { return ali; }
270    long getAliLen() const { return max_ali_len; }
271    GBDATA *get_gb_main() const { sio_assert(gb_main); return gb_main; }
272
273    void set_single_mode(GBDATA *gb_species) { single_species = gb_species; }
274    bool in_single_mode() const { return single_species; }
275
276    GBDATA *first_species() const { return single_species ? single_species : whichSpecies.select_first(gb_main); }
277    GBDATA *next_species(GBDATA *gb_prev) const { return single_species ? NULL : whichSpecies.select_next(gb_prev); }
278
279    const unsigned char *get_seq_data(GBDATA *gb_species, size_t& slen, GB_ERROR& error) const;
280    static bool isGap(char c) { return c == '-' || c == '.'; }
281
282    size_t count_species() {
283        sio_assert(!in_single_mode());
284        if (species_count == size_t(-1)) {
285            species_count = 0;
286            for (GBDATA *gb_species = whichSpecies.select_first(gb_main);
287                 gb_species;
288                 gb_species = whichSpecies.select_next(gb_species))
289            {
290                species_count++;
291            }
292        }
293        return species_count;
294    }
295
296    GB_ERROR    detectVerticalGaps();
297    const char *get_export_sequence(GBDATA *gb_species, size_t& seq_len, GB_ERROR& error);
298};
299
300const unsigned char *export_sequence_data::get_seq_data(GBDATA *gb_species, size_t& slen, GB_ERROR& err) const {
301    const char *data   = 0;
302    GBDATA     *gb_seq = GBT_find_sequence(gb_species, ali);
303
304    if (!gb_seq) {
305        err  = GBS_global_string_copy("No data in alignment '%s' of species '%s'", ali, GBT_read_name(gb_species));
306        slen = 0;
307    }
308    else {
309        data = GB_read_char_pntr(gb_seq);
310        slen = GB_read_count(gb_seq);
311        err  = 0;
312    }
313    return (const unsigned char *)data;
314}
315
316
317GB_ERROR export_sequence_data::detectVerticalGaps() {
318    GB_ERROR err = 0;
319
320    sio_assert(!in_single_mode());
321
322    if (compress == 1) {        // compress vertical gaps!
323        // @@@ detection of vertical gaps should better be done either by AP_filter directly or by FilteredExport
324
325        size_t  gap_columns = filter->get_filtered_length();
326        size_t *gap_column  = new size_t[gap_columns+1];
327
328        const size_t *filterpos_2_seqpos = filter->get_filterpos_2_seqpos();
329        memcpy(gap_column, filterpos_2_seqpos, gap_columns*sizeof(*gap_column));
330        gap_column[gap_columns] = max_ali_len;
331
332        arb_progress progress("Calculating vertical gaps", count_species());
333
334        for (GBDATA *gb_species = first_species();
335             gb_species && !err;
336             gb_species = next_species(gb_species))
337        {
338            size_t               slen;
339            const unsigned char *sdata = get_seq_data(gb_species, slen, err);
340
341            if (!err) {
342                size_t j = 0;
343                size_t i;
344                for (i = 0; i<gap_columns; ++i) {
345                    if (isGap(sdata[gap_column[i]])) {
346                        gap_column[j++] = gap_column[i]; // keep gap column
347                    }
348                    // otherwise it's overwritten
349                }
350
351                sio_assert(i >= j);
352                size_t skipped_columns  = i-j;
353                sio_assert(gap_columns >= skipped_columns);
354                gap_columns            -= skipped_columns;
355            }
356            progress.inc_and_check_user_abort(err);
357        }
358
359        if (!err) {
360            columns       = filter->get_filtered_length() - gap_columns;
361            export_column = new size_t[columns];
362
363            size_t gpos = 0;           // index into array of vertical gaps
364            size_t epos = 0;           // index into array of exported columns
365            size_t flen = filter->get_filtered_length();
366            size_t a;
367            for (a = 0; a<flen && gpos<gap_columns; ++a) {
368                size_t fpos = filterpos_2_seqpos[a];
369                if (fpos == gap_column[gpos]) { // only gaps here -> skip column
370                    gpos++;
371                }
372                else { // not only gaps -> use column
373                    sio_assert(fpos<gap_column[gpos]);
374                    sio_assert(epos < columns); // got more columns than expected
375                    export_column[epos++] = fpos;
376                }
377            }
378            for (; a<flen; ++a) {
379                export_column[epos++] = filterpos_2_seqpos[a];
380            }
381
382            sio_assert(epos == columns);
383        }
384
385        delete [] gap_column;
386    }
387    else { // compress all or none (simply use filter)
388        const size_t *filterpos_2_seqpos = filter->get_filterpos_2_seqpos();
389
390        columns       = filter->get_filtered_length();
391        export_column = new size_t[columns];
392
393        memcpy(export_column, filterpos_2_seqpos, columns*sizeof(*filterpos_2_seqpos));
394    }
395
396    seq = new char[columns+1];
397
398    return err;
399}
400
401const char *export_sequence_data::get_export_sequence(GBDATA *gb_species, size_t& seq_len, GB_ERROR& err) {
402    if (gb_species != last_species_read) {
403        freenull(error);
404
405        // read + filter a new species
406        GB_ERROR             curr_error;
407        const unsigned char *data = get_seq_data(gb_species, len, curr_error);
408
409        if (curr_error) {
410            error = strdup(curr_error);
411        }
412        else {
413            size_t       i;
414            const uchar *simplify = filter->get_simplify_table();
415
416            if (cut_stop_codon) {
417                const unsigned char *stop_codon = (const unsigned char *)memchr(data, '*', len);
418                if (stop_codon) {
419                    len = stop_codon-data;
420                }
421            }
422
423            if (compress == 2) { // compress all gaps
424                size_t j = 0;
425                for (i = 0; i<columns; ++i) {
426                    size_t seq_pos = export_column[i];
427                    if (seq_pos<len) {
428                        unsigned char c = data[seq_pos];
429                        if (!isGap(c)) {
430                            seq[j++] = simplify[c];
431                        }
432                    }
433                }
434                seq[j] = 0;
435                len    = j;
436            }
437            else { // compress vertical or compress none (simply use filter in both cases)
438                for (i = 0; i<columns; ++i) {
439                    size_t seq_pos = export_column[i];
440                    if (seq_pos<len) {
441                        seq[i] = simplify[data[seq_pos]];
442                    }
443                    else {
444                        seq[i] = simplify['.'];
445                    }
446                }
447                seq[i] = 0;
448                len    = columns;
449            }
450        }
451    }
452
453    err = error;
454    if (error) {
455        seq_len  = 0;
456        return 0;
457    }
458
459    seq_len  = len;
460    return seq;
461}
462
463// ----------------------------------------
464// exported_sequence is hooked into ACI temporary (provides result of command 'export_sequence')
465// which is the sequence filtered and compressed according to settings in the export window
466
467static export_sequence_data *esd = 0;
468
469static const char *exported_sequence(GBDATA *gb_species, size_t *seq_len, GB_ERROR *error) {
470    sio_assert(esd);
471    return esd->get_export_sequence(gb_species, *seq_len, *error);
472}
473
474static GB_ERROR XML_recursive(GBDATA *gbd, int depth) {
475    GB_ERROR    error    = 0;
476    const char *key_name = GB_read_key_pntr(gbd);
477    XML_Tag    *tag      = 0;
478    bool        descend  = true;
479
480    if (depth == 1 && strncmp(key_name, "ali_", 4) == 0) { // hack needed if seq-quality information exists
481        sio_assert(esd);
482        descend = false; // do not descend into alignments
483        if (strcmp(esd->getAlignment(), key_name) == 0) { // the wanted alignment
484
485            tag = new XML_Tag("ALIGNMENT");
486            tag->add_attribute("name", key_name+4);
487
488            GBDATA     *gb_species = GB_get_father(gbd);
489            size_t      len;
490            const char *seq        = exported_sequence(gb_species, &len, &error);
491
492            if (seq) {
493                XML_Tag dtag("data");
494                { XML_Text seqText(seq); }
495            }
496        }
497    }
498    else {
499        tag = new XML_Tag(key_name);
500
501        if (GB_is_container(gbd)) {
502            const char *name = GBT_read_char_pntr(gbd, "name");
503            if (name) tag->add_attribute("name", name);
504        }
505    }
506
507    if (descend) {
508        if (GB_read_type(gbd) == GB_DB) {
509            for (GBDATA *gb_child = GB_child(gbd); gb_child && !error; gb_child = GB_nextChild(gb_child)) {
510                const char *sub_key_name = GB_read_key_pntr(gb_child);
511
512                if (strcmp(sub_key_name, "name") != 0) { // do not recurse for "name" (is handled above)
513                    error = XML_recursive(gb_child, depth+1);
514                }
515            }
516        }
517        else {
518            char *content = GB_read_as_string(gbd);
519            if (content) {
520                XML_Text text(content);
521                free(content);
522            }
523            else {
524                tag->add_attribute("error", "unsavable");
525            }
526        }
527    }
528
529    delete tag;
530    return error;
531}
532
533static GB_ERROR export_species_using_form(FILE *out, const char *form, const GBL_call_env& callEnv) { // @@@ pass preparsed command (form)
534    GB_ERROR  error  = NULL;
535    char     *pars   = GBS_string_eval_in_env(" ", form, callEnv);
536    if (!pars) error = GB_await_error();
537    else {
538        char *p;
539        char *o = pars;
540        while ((p = GBS_find_string(o, "$$DELETE_LINE$$", 0))) {
541            char *l, *r;
542            for (l = p; l>o; l--) if (*l=='\n') break;
543            r = strchr(p, '\n'); if (!r) r = p + strlen(p);
544            fwrite(o, 1, l-o, out);
545            o = r;
546        }
547        fputs(o, out);
548        free(pars);
549    }
550    return error;
551}
552
553static GB_ERROR export_format_single(const char *db_name, const char *formname, const char *outname, char **resulting_outname) {
554    // Exports sequences specified by 'esd' (module global variable)
555    // to format specified by 'formname'.
556    //
557    // if 'outname' == NULL -> export species to temporary file, otherwise to 'outname'.
558    // Full path of generated file is returned in 'resulting_outname'
559
560    static int export_depth     = 0;
561    export_depth++;
562
563    *resulting_outname = 0;
564
565    export_format efo;
566    GB_ERROR      error = read_export_format(&efo, formname, true);
567
568    if (!error) {
569        if (!outname) {                             // if no 'outname' is given -> export to temporary file
570            char *unique_outname = GB_unique_filename("exported", efo.suffix);
571            *resulting_outname   = GB_create_tempfile(unique_outname);
572            free(unique_outname);
573
574            if (!*resulting_outname) error = GB_await_error();
575        }
576        else *resulting_outname = strdup(outname);
577    }
578
579    sio_assert(error || *resulting_outname);
580
581    if (!error) {
582        if (efo.new_format) {
583            // Export data using format 'new_format'.
584            // Afterwards convert to wanted format using 'system'.
585
586            sio_assert(efo.system);
587
588            char *intermediate_export;
589            error = export_format_single(db_name, efo.new_format, NULL, &intermediate_export);
590            if (!error) {
591                sio_assert(GB_is_privatefile(intermediate_export, false));
592
593                GB_informationf("Converting to %s", efo.suffix);
594
595                char *srt = GBS_global_string_copy("$<=%s:$>=%s", intermediate_export, *resulting_outname);
596                char *sys = GBS_string_eval(efo.system, srt);
597
598                GB_informationf("exec '%s'", efo.system);
599                error = GBK_system(sys);
600
601                GB_unlink_or_warn(intermediate_export, &error);
602
603                free(sys);
604                free(srt);
605            }
606            free(intermediate_export);
607        }
608        else {
609            FILE *out       = fopen(*resulting_outname, "wt");
610            if (!out) error = GB_IO_error("writing", *resulting_outname);
611            else {
612                XML_Document *xml = 0;
613
614                int allCount    = 0;
615                for (GBDATA *gb_species = esd->first_species();
616                     gb_species && !error;
617                     gb_species = esd->next_species(gb_species))
618                {
619                    allCount++;
620                }
621
622                arb_progress progress(allCount);
623                progress.auto_subtitles("Saving species");
624
625                if (efo.export_mode == EXPORT_XML) {
626                    xml = new XML_Document("ARB_SEQ_EXPORT", "arb_seq_export.dtd", out);
627                    {
628                        xml->add_attribute("database", db_name);
629                    }
630                    xml->add_attribute("export_date", ARB_date_string());
631                    {
632                        XML_Comment rem("There is a basic version of ARB_seq_export.dtd in $ARBHOME/lib/dtd\n"
633                                        "but you might need to expand it by yourself,\n"
634                                        "because the ARB-database may contain any kind of fields.");
635                    }
636                }
637
638                GBL_env env(esd->get_gb_main(), NULL);
639
640                for (GBDATA *gb_species = esd->first_species();
641                     gb_species && !error;
642                     gb_species = esd->next_species(gb_species))
643                {
644                    switch (efo.export_mode) {
645                        case EXPORT_USING_FORM: {
646                            GBL_call_env callEnv(gb_species, env);
647                            error = export_species_using_form(out, efo.form, callEnv);
648                            break;
649                        }
650                        case EXPORT_XML:
651                            error = XML_recursive(gb_species, 0);
652                            break;
653
654                        case EXPORT_INVALID:
655                            sio_assert(0);
656                            break;
657                    }
658                    progress.inc_and_check_user_abort(error);
659                }
660
661                delete xml;
662                fclose(out);
663            }
664        }
665    }
666
667    if (error) {
668        if (*resulting_outname) {
669            GB_unlink_or_warn(*resulting_outname, NULL);
670            freenull(*resulting_outname);
671        }
672    }
673
674    export_depth--;
675
676    return error;
677}
678
679static GB_ERROR export_format_multiple(const char* dbname, const char *formname, const char *outname, bool multiple, char **resulting_outname) {
680    GB_ERROR error = 0;
681
682    if (multiple) {
683        char *path, *name, *suffix;
684        GB_split_full_path(outname, &path, NULL, &name, &suffix);
685        *resulting_outname = NULL;
686
687        arb_progress progress("Exporting data", esd->count_species());
688
689        for (GBDATA *gb_species = esd->first_species();
690             gb_species && !error;
691             gb_species = esd->next_species(gb_species))
692        {
693            const char *species_name = GBT_read_char_pntr(gb_species, "name");
694            if (!species_name) error = "Can't export unnamed species";
695            else {
696                const char *fname = GB_append_suffix(GBS_global_string("%s_%s", name, species_name), suffix);
697                progress.subtitle(fname);
698
699                char *oname = strdup(GB_concat_path(path, fname));
700                char *res_oname;
701
702                esd->set_single_mode(gb_species); // means: only export 'gb_species'
703                error = export_format_single(dbname, formname, oname, &res_oname);
704                esd->set_single_mode(NULL);
705
706                if (!*resulting_outname || // not set yet
707                    (res_oname && strcmp(*resulting_outname, res_oname)>0)) // or smaller than set one
708                {
709                    reassign(*resulting_outname, res_oname);
710                }
711
712                free(res_oname);
713                free(oname);
714            }
715
716            progress.inc_and_check_user_abort(error);
717        }
718
719        free(suffix);
720        free(name);
721        free(path);
722    }
723    else {
724        arb_progress progress("Exporting data");
725        error = export_format_single(dbname, formname, outname, resulting_outname);
726    }
727
728    return error;
729}
730
731namespace SEQIO {
732
733    GB_ERROR export_by_format(GBDATA *gb_main, ExportWhich which, const char *one_species,
734                              AP_filter *filter, int cut_stop_codon, int compress,
735                              const char *dbname, const char *formname,
736                              const char *outname, int multiple, char **real_outname)
737    {
738        sio_assert(!GB_have_error());
739
740        GB_ERROR error = filter->is_invalid();
741        if (!error) {
742            esd = new export_sequence_data(gb_main, which, one_species, filter, cut_stop_codon, compress);
743            sio_assert(esd->getAliLen()>0);
744
745            GB_set_export_sequence_hook(exported_sequence);
746
747            error = esd->detectVerticalGaps();
748            if (!error) {
749                error = export_format_multiple(dbname, formname, outname, multiple, real_outname);
750                if (error) error = GBS_static_string(error); // error is member of export_sequence_data -> copy to static buffer
751            }
752
753            GB_set_export_sequence_hook(0);
754        }
755        delete esd;
756        esd = 0;
757
758        sio_assert(!GB_have_error());
759        return error;
760    }
761
762    GB_ERROR get_exportFormat_information(const char *eft_formname, ExportFormatInfo& info) {
763        export_format efs;
764        GB_ERROR      error = read_export_format(&efs, eft_formname, false);
765
766        if (!error) {
767            if (efs.suffix) {
768                info.suffix = efs.suffix;
769                efs.suffix  = NULL;
770            }
771            if (efs.description) {
772                info.description = efs.description;
773                efs.description  = NULL;
774            }
775        }
776
777        return error;
778    }
779
780};
781
782// --------------------------------------------------------------------------------
783
784#ifdef UNIT_TESTS
785#include <test_unit.h>
786
787// uncomment to auto-update exported files
788// (needed once after changing database or export formats)
789// #define TEST_AUTO_UPDATE
790#define TEST_AUTO_UPDATE_ONLY_MISSING // do auto-update only if file is missing
791
792void TEST_sequence_export() {
793    GB_shell              shell;
794    arb_suppress_progress silence;
795
796    GBDATA   *gb_main    = GB_open("TEST_loadsave.arb", "r");
797    char     *export_dir = nulldup(GB_path_in_ARBLIB("export"));
798    StrArray  eft;
799    GBS_read_dir(eft, export_dir, "*.eft");
800
801    AP_filter *filter = NULL;
802    {
803        GB_transaction ta(gb_main);
804
805        char   *ali    = GBT_get_default_alignment(gb_main);
806        size_t  alilen = GBT_get_alignment_len(gb_main, ali);
807        filter         = new AP_filter(alilen);
808
809        GBT_mark_all(gb_main, 0);
810        GBDATA *gb_species = GBT_find_species(gb_main, "MetMazei");
811        TEST_REJECT_NULL(gb_species);
812
813        GB_write_flag(gb_species, 1); // mark
814        free(ali);
815    }
816    for (int e = 0; eft[e]; ++e) {
817        for (int complete = 0; complete <= 1; ++complete) {
818            const char *name = strrchr(eft[e], '/');
819            TEST_REJECT_NULL(name);
820            name++;
821
822            TEST_ANNOTATE(name);
823
824            {
825                export_format efo;
826                TEST_EXPECT_NO_ERROR(read_export_format(&efo, eft[e], complete));
827                if (strcmp(name, "fasta_wacc.eft") == 0) { // test description of one filter
828                    TEST_EXPECT_EQUAL(efo.description,
829                                      "Exports sequences to fasta-format.\n"
830                                      "Header exported as: >ID SEQLENGTH bp SEQTYPE ACC");
831                }
832            }
833
834            if (complete) {
835                const char *outname      = "impexp/exported";
836                char       *used_outname = NULL;
837
838                {
839                    GB_transaction ta(gb_main);
840                    TEST_EXPECT_NO_ERROR(export_by_format(gb_main, EBF_MARKED, NULL, filter, 0, 0, "DBname", eft[e], outname, 0, &used_outname));
841                }
842
843                char *expected = GBS_global_string_copy("impexp/%s.exported", name);
844
845#if defined(TEST_AUTO_UPDATE)
846#if defined(TEST_AUTO_UPDATE_ONLY_MISSING)
847                if (GB_is_regularfile(expected)) {
848                    TEST_EXPECT_TEXTFILE_DIFFLINES_IGNORE_DATES(outname, expected, 0);
849                }
850                else
851#else
852                {
853                    TEST_COPY_FILE(outname, expected);
854                }
855#endif
856#else
857                TEST_EXPECT_TEXTFILE_DIFFLINES_IGNORE_DATES(outname, expected, 0);
858                // see ../../UNIT_TESTER/run/impexp
859#endif // TEST_AUTO_UPDATE
860                TEST_EXPECT_ZERO_OR_SHOW_ERRNO(unlink(outname));
861
862                free(expected);
863                free(used_outname);
864            }
865        }
866    }
867
868    delete filter;
869    free(export_dir);
870    GB_close(gb_main);
871}
872
873#endif // UNIT_TESTS
Note: See TracBrowser for help on using the repository browser.