source: branches/help/ARBDB/adali.cxx

Last change on this file was 19518, checked in by westram, 6 weeks ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 45.2 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : adali.cxx                                         //
4//   Purpose   : alignments                                        //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include <arbdbt.h>
12#include <adGene.h>
13
14#include "gb_local.h"
15
16#include <arb_strarray.h>
17#include <arb_str.h>
18#include <arb_global_defs.h>
19
20static void check_for_species_without_data(const char *species_name, long value, void *counterPtr) {
21    if (value == 1) {
22        long cnt = *((long*)counterPtr);
23        if (cnt<40) {
24            GB_warningf("Species '%s' has no data in any alignment", species_name);
25        }
26        *((long*)counterPtr) = cnt+1;
27    }
28}
29
30GBDATA *GBT_get_presets(GBDATA *gb_main) {
31    return GBT_find_or_create(gb_main, "presets", 7);
32}
33
34int GBT_count_alignments(GBDATA *gb_main) {
35    int     count      = 0;
36    GBDATA *gb_presets = GBT_get_presets(gb_main);
37    for (GBDATA *gb_ali = GB_entry(gb_presets, "alignment");
38         gb_ali;
39         gb_ali = GB_nextEntry(gb_ali))
40    {
41        ++count;
42    }
43    return count;
44}
45
46static GB_ERROR GBT_check_alignment(GBDATA *gb_main, GBDATA *preset_alignment, GB_HASH *species_name_hash) {
47    /* check
48     * - whether alignment has correct size and
49     * - whether all data is present.
50     *
51     * Sets the security deletes and writes.
52     *
53     * If 'species_name_hash' is not NULp,
54     * - it initially has to contain value == 1 for each existing species.
55     * - afterwards it will contain value  == 2 for each species where an alignment has been found.
56     */
57
58    GBDATA *gb_species_data  = GBT_get_species_data(gb_main);
59    GBDATA *gb_extended_data = GBT_get_SAI_data(gb_main);
60
61    GB_ERROR  error      = NULp;
62    char     *ali_name   = GBT_read_string(preset_alignment, "alignment_name");
63    if (!ali_name) error = "Alignment w/o 'alignment_name'";
64
65    if (!error) {
66        long    security_write = -1;
67        long    stored_ali_len = -1;
68        long    found_ali_len  = -1;
69        long    aligned        = 1;
70        GBDATA *gb_ali_len     = NULp;
71
72        {
73            GBDATA *gb_ali_wsec = GB_entry(preset_alignment, "alignment_write_security");
74            if (!gb_ali_wsec) {
75                error = "has no 'alignment_write_security' entry";
76            }
77            else {
78                security_write = GB_read_int(gb_ali_wsec);
79            }
80        }
81
82
83        if (!error) {
84            gb_ali_len = GB_entry(preset_alignment, "alignment_len");
85            if (!gb_ali_len) {
86                error = "has no 'alignment_len' entry";
87            }
88            else {
89                stored_ali_len = GB_read_int(gb_ali_len);
90            }
91        }
92
93        if (!error) {
94            GBDATA *gb_species;
95            for (gb_species = GBT_first_species_rel_species_data(gb_species_data);
96                 gb_species && !error;
97                 gb_species = GBT_next_species(gb_species))
98            {
99                GBDATA     *gb_name        = GB_entry(gb_species, "name");
100                const char *name           = NULp;
101                int         alignment_seen = 0;
102
103                if (!gb_name) {
104                    // fatal: name is missing -> create a unique name
105                    char *unique = GBT_create_unique_species_name(gb_main, "autoname.");
106                    error        = GBT_write_string(gb_species, "name", unique);
107
108                    if (!error) {
109                        gb_name = GB_entry(gb_species, "name");
110                        GBS_write_hash(species_name_hash, unique, 1); // not seen before
111                        GB_warningf("Seen unnamed species (gave name '%s')", unique);
112                    }
113                    free(unique);
114                }
115
116                if (!error) {
117                    name = GB_read_char_pntr(gb_name);
118                    if (species_name_hash) {
119                        int seen = GBS_read_hash(species_name_hash, name);
120
121                        gb_assert(seen != 0); // species_name_hash not initialized correctly
122                        if (seen == 2) alignment_seen = 1; // already seen an alignment
123                    }
124                }
125
126                if (!error) {
127                    GB_topSecurityLevel unsecured(gb_name);
128
129                    error             = GB_write_security_delete(gb_name, 7);
130                    if (!error) error = GB_write_security_write(gb_name, 6);
131
132                    if (!error) {
133                        GBDATA *gb_ali = GB_entry(gb_species, ali_name);
134                        if (gb_ali) {
135                            GBDATA *gb_data = GB_entry(gb_ali, "data");
136                            if (!gb_data) {
137                                error = GBT_write_string(gb_ali, "data", "Error: entry 'data' was missing and therefore was filled with this text.");
138                                GB_warningf("No '%s/data' entry for species '%s' (has been filled with dummy data)", ali_name, name);
139                            }
140                            else {
141                                if (GB_read_type(gb_data) != GB_STRING) {
142                                    GB_delete(gb_data);
143                                    error = GBS_global_string("'%s/data' of species '%s' had wrong DB-type (%s) and has been deleted!",
144                                                              ali_name, name, GB_read_key_pntr(gb_data));
145                                }
146                                else {
147                                    long data_len = GB_read_string_count(gb_data);
148                                    if (found_ali_len != data_len) {
149                                        if (found_ali_len>0)        aligned       = 0;
150                                        if (found_ali_len<data_len) found_ali_len = data_len;
151                                    }
152
153                                    error = GB_write_security_delete(gb_data, 7);
154
155                                    if (!alignment_seen && species_name_hash) { // mark as seen
156                                        GBS_write_hash(species_name_hash, name, 2); // 2 means "species has data in at least 1 alignment"
157                                    }
158                                }
159                            }
160                        }
161                    }
162
163                    if (!error) error = GB_write_security_delete(gb_species, security_write);
164                }
165            }
166        }
167
168        if (!error) {
169            GBDATA *gb_sai;
170            for (gb_sai = GBT_first_SAI_rel_SAI_data(gb_extended_data);
171                 gb_sai && !error;
172                 gb_sai = GBT_next_SAI(gb_sai))
173            {
174                GBDATA *gb_sai_name = GB_entry(gb_sai, "name");
175                GBDATA *gb_ali;
176
177                if (!gb_sai_name) continue;
178
179                GB_write_security_delete(gb_sai_name, 7);
180
181                gb_ali = GB_entry(gb_sai, ali_name);
182                if (gb_ali) {
183                    GBDATA *gb_sai_data;
184                    for (gb_sai_data = GB_child(gb_ali);
185                         gb_sai_data;
186                         gb_sai_data = GB_nextChild(gb_sai_data))
187                    {
188                        long type = GB_read_type(gb_sai_data);
189                        long data_len;
190
191                        if (type == GB_DB || type < GB_BITS) continue;
192                        if (GB_read_key_pntr(gb_sai_data)[0] == '_') continue; // e.g. _STRUCT (of secondary structure)
193
194                        data_len = GB_read_count(gb_sai_data);
195
196                        if (found_ali_len != data_len) {
197                            if (found_ali_len>0)        aligned       = 0;
198                            if (found_ali_len<data_len) found_ali_len = data_len;
199                        }
200                    }
201                }
202            }
203        }
204
205        if (!error && stored_ali_len != found_ali_len) error = GB_write_int(gb_ali_len, found_ali_len);
206        if (!error) error = GBT_write_int(preset_alignment, "aligned", aligned);
207
208        if (error) {
209            error = GBS_global_string("Error checking alignment '%s':\n%s\n",  ali_name, error);
210        }
211    }
212
213    free(ali_name);
214
215    return error;
216}
217
218GB_ERROR GBT_check_data(GBDATA *Main, const char *alignment_name) {
219    /* alignment_name
220     *  == 0     -> check all existing alignments
221     * otherwise -> check only one alignment
222     */
223
224    GB_ERROR  error             = NULp;
225    GBDATA   *gb_sd             = GBT_get_species_data(Main);
226    GBDATA   *gb_presets        = GBT_get_presets(Main);
227    GB_HASH  *species_name_hash = NULp;
228
229    // create rest of main containers
230    GBT_get_SAI_data(Main);
231    GBT_get_tree_data(Main);
232
233    if (alignment_name) {
234        GBDATA *gb_ali_name = GB_find_string(gb_presets, "alignment_name", alignment_name, GB_IGNORE_CASE, SEARCH_GRANDCHILD);
235        if (!gb_ali_name) {
236            error = GBS_global_string("Alignment '%s' does not exist - it can't be checked.", alignment_name);
237        }
238    }
239
240    if (!error) {
241        // check whether we have an default alignment
242        GBDATA *gb_use = GB_entry(gb_presets, "use");
243        if (!gb_use) {
244            // if we have no default alignment -> look for any alignment
245            GBDATA *gb_ali_name = GB_find_string(gb_presets, "alignment_name", alignment_name, GB_IGNORE_CASE, SEARCH_GRANDCHILD);
246
247            error = gb_ali_name ?
248                GBT_write_string(gb_presets, "use", GB_read_char_pntr(gb_ali_name)) :
249                "No alignment defined";
250        }
251    }
252
253    if (!alignment_name && !error) {
254        // if all alignments are checked -> use species_name_hash to detect duplicated species and species w/o data
255        long duplicates = 0;
256        long unnamed    = 0;
257
258        species_name_hash = GBS_create_hash(GBT_get_species_count(Main), GB_IGNORE_CASE);
259
260        if (!error) {
261            for (GBDATA *gb_species = GBT_first_species_rel_species_data(gb_sd);
262                 gb_species;
263                 gb_species = GBT_next_species(gb_species))
264            {
265                const char *name = GBT_get_name(gb_species);
266                if (name) {
267                    if (GBS_read_hash(species_name_hash, name)) duplicates++;
268                    GBS_incr_hash(species_name_hash, name);
269                }
270                else {
271                    unnamed++;
272                }
273            }
274        }
275
276        if (duplicates) {
277            error = GBS_global_string("Database is corrupted:\n"
278                                      "Found %li duplicated species with identical names!\n"
279                                      "Fix the problem using\n"
280                                      "   'Search For Equal Fields and Mark Duplicates'\n"
281                                      "in ARB_NTREE search tool, save DB and restart ARB.",
282                                      duplicates);
283        }
284        else if (unnamed) {
285            error = GBS_global_string("Database is corrupted:\n"
286                                      "Found %li species without IDs ('name')!\n"
287                                      "This is a sewere problem!\n"
288                                      "ARB will not work as expected!",
289                                      unnamed);
290        }
291    }
292
293    if (!error) {
294        for (GBDATA *gb_ali = GB_entry(gb_presets, "alignment");
295             gb_ali && !error;
296             gb_ali = GB_nextEntry(gb_ali))
297        {
298            error = GBT_check_alignment(Main, gb_ali, species_name_hash);
299        }
300    }
301
302    if (species_name_hash) {
303        if (!error) {
304            long counter = 0;
305            GBS_hash_do_const_loop(species_name_hash, check_for_species_without_data, &counter);
306            if (counter>0) {
307                GB_warningf("Found %li species without alignment data (only some were listed)", counter);
308            }
309        }
310
311        GBS_free_hash(species_name_hash);
312    }
313
314    return error;
315}
316
317void GBT_get_alignment_names(ConstStrArray& names, GBDATA *gbd) {
318    /* Get names of existing alignments from database.
319     *
320     * Returns: array of strings, the last element is NULp
321     */
322
323    GBDATA *presets = GBT_get_presets(gbd);
324    for (GBDATA *ali = GB_entry(presets, "alignment"); ali; ali = GB_nextEntry(ali)) {
325        GBDATA *name = GB_entry(ali, "alignment_name");
326        names.put(name ? GB_read_char_pntr(name) : "<unnamed alignment>");
327    }
328}
329
330static char *gbt_nonexisting_alignment(GBDATA *gbMain) {
331    char  *ali_other = NULp;
332    int    counter;
333
334    for (counter = 1; !ali_other; ++counter) {
335        ali_other = GBS_global_string_copy("ali_x%i", counter);
336        if (GBT_get_alignment(gbMain, ali_other)) freenull(ali_other); // exists -> continue
337    }
338
339    GB_clear_error(); // from GBT_get_alignment
340    return ali_other;
341}
342
343GB_ERROR GBT_check_alignment_name(const char *alignment_name) {
344    GB_ERROR error = GB_check_key(alignment_name);
345    if (!error && !ARB_strBeginsWith(alignment_name, "ali_")) {
346        error = GBS_global_string("alignment name '%s' has to start with 'ali_'", alignment_name);
347    }
348    return error;
349}
350
351static GB_ERROR create_ali_strEntry(GBDATA *gb_ali, const char *field, const char *strval, long write_protection) {
352    GB_ERROR  error  = NULp;
353    GBDATA   *gb_sub = GB_create(gb_ali, field, GB_STRING);
354
355    if (!gb_sub) error = GB_await_error();
356    else {
357        error             = GB_write_string(gb_sub, strval);
358        if (!error) error = GB_write_security_delete(gb_sub, 7);
359        if (!error) error = GB_write_security_write(gb_sub, write_protection);
360    }
361
362    if (error) {
363        error = GBS_global_string("failed to create alignment subentry '%s'\n"
364                                  "(Reason: %s)", field, error);
365    }
366
367    return error;
368}
369static GB_ERROR create_ali_intEntry(GBDATA *gb_ali, const char *field, int intval, long write_protection) {
370    GB_ERROR  error  = NULp;
371    GBDATA   *gb_sub = GB_create(gb_ali, field, GB_INT);
372
373    if (!gb_sub) error = GB_await_error();
374    else {
375        error             = GB_write_int(gb_sub, intval);
376        if (!error) error = GB_write_security_delete(gb_sub, 7);
377        if (!error) error = GB_write_security_write(gb_sub, write_protection);
378    }
379
380    if (error) {
381        error = GBS_global_string("failed to create alignment subentry '%s'\n"
382                                  "(Reason: %s)", field, error);
383    }
384
385    return error;
386}
387
388GBDATA *GBT_create_new_alignment(GBDATA *gb_main, const char *name, long len, long aligned, long security, const char *type, const char *why_created) {
389    /* create alignment
390     *
391     * returns pointer to alignment or
392     * NULp (in this case an error has been exported)
393     */
394    GB_ERROR  error      = NULp;
395    GBDATA   *gb_presets = GBT_get_presets(gb_main);
396    GBDATA   *result     = NULp;
397
398    if (!gb_presets) {
399        error = GBS_global_string("can't find/create 'presets' (Reason: %s)", GB_await_error());
400    }
401    else {
402        error = GBT_check_alignment_name(name);
403        if (!error && (security<0 || security>6)) {
404            error = GBS_global_string("Illegal security value %li (allowed 0..6)", security);
405        }
406        if (!error) {
407            const char *allowed_types = ":dna:rna:ami:usr:";
408            int         tlen          = strlen(type);
409            const char *found         = strstr(allowed_types, type);
410            if (!found || found == allowed_types || found[-1] != ':' || found[tlen] != ':') {
411                error = GBS_global_string("Invalid alignment type '%s'", type);
412            }
413        }
414
415        if (!error) {
416            GBDATA *gb_name = GB_find_string(gb_presets, "alignment_name", name, GB_IGNORE_CASE, SEARCH_GRANDCHILD);
417
418            if (gb_name) error = GBS_global_string("Alignment '%s' already exists", name);
419            else {
420                GBDATA *gb_ali     = GB_create_container(gb_presets, "alignment");
421                if (!gb_ali) error = GB_await_error();
422                else {
423                    char *remark = GBS_global_string_copy("%s: alignment created %s", ARB_date_string(), why_created);
424
425                    error             = GB_write_security_delete(gb_ali, 6);
426                    if (!error) error = create_ali_strEntry(gb_ali, "alignment_name",           name, 6);
427                    if (!error) error = create_ali_intEntry(gb_ali, "alignment_len",            len, 0);
428                    if (!error) error = create_ali_intEntry(gb_ali, "aligned",                  aligned <= 0 ? 0 : 1, 0);
429                    if (!error) error = create_ali_intEntry(gb_ali, "alignment_write_security", security, 6);
430                    if (!error) error = create_ali_strEntry(gb_ali, "alignment_type",           type, 0);
431                    if (!error) error = create_ali_strEntry(gb_ali, "alignment_rem",            remark, 0);
432
433                    free(remark);
434                }
435
436                if (!error) result = gb_ali;
437            }
438        }
439    }
440
441    if (!result) {
442        gb_assert(error);
443        GB_export_errorf("in GBT_create_new_alignment: %s", error);
444    }
445#if defined(DEBUG)
446    else gb_assert(!error);
447#endif // DEBUG
448
449    return result;
450}
451
452GBDATA *GBT_create_alignment(GBDATA *gb_main, const char *name, long len, long aligned, long security, const char *type) {
453    // goes to header: __ATTR__DEPRECATED_TODO("better use GBT_create_new_alignment()")
454    //
455    // This method restores compatibility with older binaries (esp. sina 1.3).
456    // Reverts incompatibility introduced by r19146.
457
458    return GBT_create_new_alignment(gb_main, name, len, aligned, security, type,
459                                    "created by outdated non-arb-binary");
460}
461
462enum CopyMoveDelMode { COPY, MOVE, DELETE };
463
464static GB_ERROR gbt_rename_alignment_of_item(GBDATA *gb_item_container, const char *item_name, const char *item_entry_name,
465                                             const char *source, const char *dest, CopyMoveDelMode mode)
466{
467    GB_ERROR  error = NULp;
468    GBDATA   *gb_item;
469
470    for (gb_item = GB_entry(gb_item_container, item_entry_name);
471         gb_item && !error;
472         gb_item = GB_nextEntry(gb_item))
473    {
474        GBDATA *gb_ali = GB_entry(gb_item, source);
475        if (!gb_ali) continue;
476
477        if (mode != DELETE) {
478            GBDATA *gb_new = GB_entry(gb_item, dest);
479            if (gb_new) {
480                error = GBS_global_string("Entry '%s' already exists", dest);
481            }
482            else {
483                gb_new             = GB_create_container(gb_item, dest);
484                if (!gb_new) error = GB_await_error();
485                else error         = GB_copy_dropProtectMarksAndTempstate(gb_new, gb_ali);
486            }
487        }
488        if (mode != COPY) error = GB_delete(gb_ali);
489    }
490
491    if (error && gb_item) {
492        UNCOVERED();
493        error = GBS_global_string("%s\n(while renaming alignment for %s '%s')", error, item_name, GBT_get_name_or_description(gb_item));
494    }
495
496    return error;
497}
498
499static GB_ERROR copy_move_del_alignment(GBDATA *gbMain, const char *source, const char *dest, CopyMoveDelMode mode) {
500    // copies, moves or deletes alignments.
501    // affects all data of that alignment in species and SAIs.
502
503    gb_assert(!GB_have_error()); // illegal to enter this function when an error is exported!
504
505    GB_ERROR error         = NULp;
506    bool     is_case_error = false;
507
508    GB_transaction ta(gbMain);
509
510    gb_assert(correlated(mode == DELETE, dest == NULp)); // dest shall be NULp for DELETE + !NULp otherwise.
511
512    GBDATA *gb_presets       = GBT_get_presets(gbMain);
513    GBDATA *gb_species_data  = gb_presets ? GBT_get_species_data(gbMain) : NULp;
514    GBDATA *gb_extended_data = gb_species_data ? GBT_get_SAI_data(gbMain) : NULp;
515
516    if (!gb_extended_data) error = GB_await_error();
517
518    // create copy and/or delete old alignment description
519    if (!error) {
520        GBDATA *gb_old_alignment = GBT_get_alignment(gbMain, source);
521
522        if (!gb_old_alignment) {
523            error = GB_await_error();
524        }
525        else {
526            if (mode != DELETE) {
527                // copy source -> dest
528                GBDATA *gbh = GBT_get_alignment(gbMain, dest);
529                if (gbh) {
530                    error         = GBS_global_string("destination alignment '%s' already exists", dest);
531                    is_case_error = (strcasecmp(source, dest) == 0); // test for case-only difference
532                }
533                else {
534                    GB_clear_error(); // from GBT_get_alignment
535                    error = GBT_check_alignment_name(dest);
536                    if (!error) {
537                        GBDATA *gb_new_alignment = GB_create_container(gb_presets, "alignment");
538                        error                    = GB_copy_dropProtectMarksAndTempstate(gb_new_alignment, gb_old_alignment);
539                        if (!error) error        = GBT_write_string(gb_new_alignment, "alignment_name", dest);
540                    }
541                }
542            }
543
544            if (mode != COPY && !error) {
545                // delete source
546                error = GB_delete(gb_old_alignment);
547            }
548        }
549    }
550
551    // change default alignment
552    if (!error && mode == MOVE) {
553        error = GBT_write_string(gb_presets, "use", dest);
554    }
555
556    // copy and/or delete alignment entries in species
557    if (!error) {
558        error = gbt_rename_alignment_of_item(gb_species_data, "Species", "species", source, dest, mode);
559    }
560
561    // copy and/or delete alignment entries in SAIs
562    if (!error) {
563        error = gbt_rename_alignment_of_item(gb_extended_data, "SAI", "extended", source, dest, mode);
564    }
565
566    if (is_case_error) {
567        gb_assert(mode != DELETE);
568        if (mode==COPY) {
569            error = "Cannot copy alignment if destination name only differs in case.";
570        }
571        else {
572            gb_assert(!GB_have_error());
573
574            // alignments source and dest only differ in case
575            char *ali_other = gbt_nonexisting_alignment(gbMain);
576
577            gb_assert(mode==MOVE);
578            gb_assert(!GB_have_error());
579
580            printf("Renaming alignment '%s' -> '%s' -> '%s' (to avoid case-problem)\n", source, ali_other, dest);
581
582            error             = copy_move_del_alignment(gbMain, source,    ali_other, MOVE);
583            if (!error) error = copy_move_del_alignment(gbMain, ali_other, dest,      MOVE);
584
585            free(ali_other);
586        }
587    }
588
589    error = ta.close(error);
590
591    return error;
592}
593
594GB_ERROR GBT_copy_alignment(GBDATA *gbMain, const char *source, const char *dest) {
595    return copy_move_del_alignment(gbMain, source, dest, COPY);
596}
597GB_ERROR GBT_rename_alignment(GBDATA *gbMain, const char *source, const char *dest) {
598    return copy_move_del_alignment(gbMain, source, dest, MOVE);
599}
600GB_ERROR GBT_delete_alignment(GBDATA *gbMain, const char *source) {
601    return copy_move_del_alignment(gbMain, source, NULp, DELETE);
602}
603
604// -----------------------------------------
605//      alignment related item functions
606
607NOT4PERL GBDATA *GBT_add_data(GBDATA *species, const char *ali_name, const char *key, GB_TYPES type) {
608    // goes to header: __ATTR__DEPRECATED_TODO("better use GBT_create_sequence_data()")
609
610    /* replace this function by GBT_create_sequence_data
611     * the same as GB_search(species, 'ali_name/key', GB_CREATE)
612     *
613     * Note: The behavior is weird, cause it does sth special for GB_STRING (write default content "...")
614     *
615     * returns create database entry (or NULp; exports an error in this case)
616     */
617
618    GB_ERROR error = GB_check_key(ali_name);
619    if (error) {
620        error = GBS_global_string("Invalid alignment name '%s' (Reason: %s)", ali_name, error);
621    }
622    else {
623        error = GB_check_hkey(key);
624        if (error) {
625            error = GBS_global_string("Invalid field name '%s' (Reason: %s)", key, error);
626        }
627    }
628
629    GBDATA *gb_data = NULp;
630    if (error) {
631        GB_export_error(error);
632    }
633    else {
634        GBDATA *gb_gb     = GB_entry(species, ali_name);
635        if (!gb_gb) gb_gb = GB_create_container(species, ali_name);
636
637        if (gb_gb) {
638            if (type == GB_STRING) {
639                gb_data = GB_search(gb_gb, key, GB_FIND);
640                if (!gb_data) {
641                    gb_data = GB_search(gb_gb, key, GB_STRING);
642                    GB_write_string(gb_data, "...");
643                }
644            }
645            else {
646                gb_data = GB_search(gb_gb, key, type);
647            }
648        }
649    }
650    return gb_data;
651}
652
653NOT4PERL GBDATA *GBT_create_sequence_data(GBDATA *species, const char *ali_name, const char *key, GB_TYPES type, int security_write) {
654    GBDATA *gb_data = GBT_add_data(species, ali_name, key, type);
655    if (gb_data) {
656        GB_ERROR error = GB_write_security_write(gb_data, security_write);
657        if (error) {
658            GB_export_error(error);
659            gb_data = NULp;
660        }
661    }
662    return gb_data;
663}
664
665GBDATA *GBT_gen_accession_number(GBDATA *gb_species, const char *ali_name) {
666    GBDATA *gb_acc = GB_entry(gb_species, "acc");
667    if (!gb_acc) {
668        GBDATA *gb_data = GBT_find_sequence(gb_species, ali_name);
669        if (gb_data) {                                     // found a valid alignment
670            GB_CSTR     sequence = GB_read_char_pntr(gb_data);
671            long        id       = GBS_checksum(sequence, 1, ".-");
672            const char *acc      = GBS_global_string("ARB_%lX", id);
673            GB_ERROR    error    = GBT_write_string(gb_species, "acc", acc);
674
675            if (error) GB_export_error(error);
676        }
677    }
678    return gb_acc;
679}
680
681
682int GBT_is_partial(GBDATA *gb_species, int default_value, bool define_if_undef) {
683    // checks whether a species has a partial or full sequence
684    //
685    // Note: partial sequences should not be used for tree calculations
686    //
687    // returns: 0 if sequence is full
688    //          1 if sequence is partial
689    //          -1 in case of error (which is exported in this case)
690    //
691    // if the sequence has no 'ARB_partial' entry it returns 'default_value'
692    // if 'define_if_undef' is true then create an 'ARB_partial'-entry with the default value
693
694    int       result     = -1;
695    GB_ERROR  error      = NULp;
696    GBDATA   *gb_partial = GB_entry(gb_species, "ARB_partial");
697
698    if (gb_partial) {
699        result = GB_read_int(gb_partial);
700        if (result != 0 && result != 1) {
701            error = "Illegal value for 'ARB_partial' (only 1 or 0 allowed)";
702        }
703    }
704    else {
705        if (define_if_undef) {
706            error = GBT_write_int(gb_species, "ARB_partial", default_value);
707        }
708        result = default_value;
709    }
710
711    if (error) {
712        GB_export_error(error);
713        return -1;
714    }
715    return result;
716}
717
718GBDATA *GBT_find_sequence(GBDATA *gb_species, const char *aliname) {
719    GBDATA *gb_ali = GB_entry(gb_species, aliname);
720    return gb_ali ? GB_entry(gb_ali, "data") : NULp;
721}
722
723static char *get_default_alignment(GBDATA *gb_main, bool use_startup_ali) {
724    /*! behavior @see GBT_get_default_alignment.
725     *  @param use_startup_ali if 'true' -> always return the value seen when first called with 'true' + make calls with 'false' always return an error.
726     *  The intention is to be able to force that an applications sticks with the
727     *  alignment found at startup and ignores if alignment is changed in arb_ntree.
728     */
729    gb_assert(!GB_have_error()); // illegal to enter this function when an error is exported!
730
731    static SmartCharPtr startup_ali_name; // set once (when function is first called with 'use_startup_ali'==true)
732
733    char *ali_name = NULp;
734    if (startup_ali_name.isSet()) {
735        ali_name = ARB_strdup(&*startup_ali_name);
736    }
737    else {
738        ali_name = GBT_read_string(gb_main, GB_DEFAULT_ALIGNMENT);
739        if (ali_name) {
740            if (strcmp(ali_name, NO_ALI_SELECTED) == 0) {
741                GB_export_error("No alignment is selected.");
742                freenull(ali_name);
743            }
744            else if (!ARB_strBeginsWith(ali_name, "ali_")) {
745                GB_export_errorf("Selected alignment '%s' is not valid!", ali_name);
746                freenull(ali_name);
747            }
748        }
749        if (ali_name && use_startup_ali) {
750            // first sucessful call with 'use_startup_ali==true'
751            startup_ali_name = ARB_strdup(ali_name); // this will remain default until program terminates
752        }
753    }
754    gb_assert(contradicted(ali_name, GB_have_error())); // either result or error!
755    return ali_name;
756}
757char *GBT_get_default_alignment(GBDATA *gb_main) {
758    /*! @return default alignment
759     * returns
760     * - the alignment that was selected in ARB alignment admin, when GBT_get_startup_alignment was called first OR
761     * - the alignment currently selected in ARB alignment admin OR
762     * - NULp if NO (valid) alignment selected.
763     *   An error is exported in that case!
764     */
765    return get_default_alignment(gb_main, false);
766}
767char *GBT_get_startup_alignment(GBDATA *gb_main) {
768    /*! same interface as GBT_get_default_alignment.
769     *  When this was called once w/o error, calls to GBT_get_default_alignment always return
770     *  the alignment which was default then.
771     */
772    return get_default_alignment(gb_main, true);
773}
774
775GB_ERROR GBT_set_default_alignment(GBDATA *gb_main, const char *alignment_name) {
776    return GBT_write_string(gb_main, GB_DEFAULT_ALIGNMENT, alignment_name);
777}
778GB_ERROR GBT_set_startup_alignment(GBDATA *gb_main, const char *alignment_name) {
779    // Similar to GBT_set_default_alignment, but does not change database default alignment.
780    // Instead it changes the application-local alignment which is derived from default
781    // alignment and "freezed" on first request (by using GBT_get_startup_alignment).
782    //
783    // Warning: GBT_set_startup_alignment fails if GBT_get_startup_alignment was already called!
784
785    GB_ERROR  error       = NULp;
786    char     *default_ali = GBT_get_default_alignment(gb_main);
787
788    bool freeze_and_reset = false;
789    if (!default_ali) {
790        GB_clear_error();
791        default_ali      = ARB_strdup(NO_ALI_SELECTED);
792        freeze_and_reset = true;
793    }
794    else {
795        freeze_and_reset = strcmp(default_ali, alignment_name) != 0;
796    }
797    if (freeze_and_reset) {
798        error = GBT_set_default_alignment(gb_main, alignment_name);
799        if (!error) {
800            char *startup_ali       = GBT_get_startup_alignment(gb_main);
801            if (!startup_ali) error = GB_await_error();             // most unlikely
802            else {
803                if (strcmp(startup_ali, alignment_name) != 0) {
804                    error = GBS_global_string("Cannot freeze alignment to '%s' (already was frozen to '%s' before)", alignment_name, startup_ali);
805                }
806                // fine, the correct alignment is freezed!
807                free(startup_ali);
808            }
809        }
810        // undo (tmp) change to default alignment:
811        GB_ERROR minerr = GBT_set_default_alignment(gb_main, default_ali);
812        error           = error ? error : minerr;
813    }
814    free(default_ali);
815    return error;
816}
817
818GBDATA *GBT_get_alignment(GBDATA *gb_main, const char *aliname) {
819    /*! @return global alignment container for alignment 'aliname' or
820     * NULp if alignment not found (error exported in that case)
821     */
822
823    GBDATA *gb_ali = NULp;
824    if (!aliname || strcmp(aliname, NO_ALI_SELECTED) == 0) {
825        GB_export_error("no alignment selected");
826    }
827    else {
828        GBDATA *gb_presets        = GBT_get_presets(gb_main);
829        GBDATA *gb_alignment_name = GB_find_string(gb_presets, "alignment_name", aliname, GB_IGNORE_CASE, SEARCH_GRANDCHILD);
830
831        if (!gb_alignment_name) {
832            GB_export_errorf("alignment '%s' not found", aliname);
833        }
834        else {
835            gb_ali = GB_get_father(gb_alignment_name);
836        }
837    }
838    gb_assert(contradicted(gb_ali, GB_have_error())); // either result or error!
839    return gb_ali;
840}
841
842// @@@ recode and change result type to long* ?
843long GBT_get_alignment_len(GBDATA *gb_main, const char *aliname) {
844    /*! @return length (>0) of alignment 'aliname' or
845     * -1 if alignment not found (error exported in that case)
846     *  0 if alignment length is zero (=invalid; error exported in that case)
847     */
848    GBDATA *gb_alignment = GBT_get_alignment(gb_main, aliname);
849    long    len          = gb_alignment ? *GBT_read_int(gb_alignment, "alignment_len") : -1;
850
851    if (len == 0) GB_export_errorf("alignment '%s' has no data", aliname);
852
853    gb_assert(contradicted(len>0, GB_have_error())); // either valid result or error!
854
855    return len;
856}
857
858GB_ERROR GBT_set_alignment_len(GBDATA *gb_main, const char *aliname, long new_len) {
859    GB_ERROR  error        = NULp;
860    GBDATA   *gb_alignment = GBT_get_alignment(gb_main, aliname);
861
862    if (!gb_alignment) {
863        error = GB_await_error();
864    }
865    else {
866        GB_topSecurityLevel unsecured(gb_main);
867        error             = GBT_write_int(gb_alignment, "alignment_len", new_len); // write new len
868        if (!error) error = GBT_write_int(gb_alignment, "aligned", 0);             // mark as unaligned
869    }
870    return error;
871}
872
873char *GBT_get_alignment_type_string(GBDATA *gb_main, const char *aliname) {
874    /*! @return type-string of alignment 'aliname' or
875     * NULp if alignment not found (error exported in that case)
876     */
877    GBDATA *gb_alignment = GBT_get_alignment(gb_main, aliname);
878    char   *result       = gb_alignment ? GBT_read_string(gb_alignment, "alignment_type") : NULp;
879
880    if (!result && !GB_have_error()) {
881        GB_export_errorf("Alignment '%s' has no alignment_type defined", aliname);
882    }
883
884    gb_assert(contradicted(result, GB_have_error())); // either result or error!
885    return result;
886}
887
888GB_alignment_type GBT_get_alignment_type(GBDATA *gb_main, const char *aliname) {
889    /*! @return type of alignment as enum type (GB_alignment_type).
890     *  GB_AT_UNKNOWN if alignment not found (error exported in that case)
891     */
892    char              *ali_type = GBT_get_alignment_type_string(gb_main, aliname);
893    GB_alignment_type  at       = GB_AT_UNKNOWN;
894
895    if (ali_type) {
896        switch (ali_type[0]) {
897            case 'r': if (strcmp(ali_type, "rna")==0) at = GB_AT_RNA; break;
898            case 'd': if (strcmp(ali_type, "dna")==0) at = GB_AT_DNA; break;
899            case 'a': if (strcmp(ali_type, "ami")==0) at = GB_AT_AA; break;
900            case 'p': if (strcmp(ali_type, "pro")==0) at = GB_AT_AA; break;
901            default: gb_assert(0); break;
902        }
903        free(ali_type);
904    }
905    return at;
906}
907
908bool GBT_is_alignment_protein(GBDATA *gb_main, const char *alignment_name) {
909    GB_alignment_type ali_type = GBT_get_alignment_type(gb_main, alignment_name);
910    gb_assert(ali_type != GB_AT_UNKNOWN);
911    return ali_type == GB_AT_AA;
912}
913
914// -----------------------
915//      gene sequence
916
917static const char *gb_cache_genome(GBDATA *gb_genome) {
918    static GBDATA *gb_last_genome = NULp;
919    static char   *last_genome    = NULp;
920
921    if (gb_genome != gb_last_genome) {
922        free(last_genome);
923
924        last_genome    = GB_read_string(gb_genome);
925        gb_last_genome = gb_genome;
926    }
927
928    return last_genome;
929}
930
931struct gene_part_pos {
932    int            parts;       // initialized for parts
933    unsigned char *certain;     // contains parts "=" chars
934    char           offset[256];
935};
936
937static gene_part_pos *gpp = NULp;
938
939static void init_gpp(int parts) {
940    if (!gpp) {
941        int i;
942        ARB_alloc(gpp, 1);
943        gpp->certain = NULp;
944
945        for (i = 0; i<256; ++i) gpp->offset[i] = 0;
946
947        gpp->offset[(int)'+'] = 1;
948        gpp->offset[(int)'-'] = -1;
949    }
950    else {
951        if (parts>gpp->parts) freenull(gpp->certain);
952    }
953
954    if (!gpp->certain) {
955        int forParts           = parts+10;
956        ARB_alloc(gpp->certain, forParts+1);
957        memset(gpp->certain, '=', forParts);
958        gpp->certain[forParts] = 0;
959        gpp->parts             = forParts;
960    }
961}
962
963static void getPartPositions(const GEN_position *pos, int part, size_t *startPos, size_t *stopPos) {
964    // returns 'startPos' and 'stopPos' of one part of a gene
965    gb_assert(part<pos->parts);
966    *startPos = pos->start_pos[part]+gpp->offset[(pos->start_uncertain ? pos->start_uncertain : gpp->certain)[part]];
967    *stopPos  = pos->stop_pos [part]+gpp->offset[(pos->stop_uncertain  ? pos->stop_uncertain  : gpp->certain)[part]];
968}
969
970NOT4PERL char *GBT_read_gene_sequence_and_length(GBDATA *gb_gene, bool use_revComplement, char partSeparator, size_t *gene_length) {
971    // return the sequence data of a gene
972    //
973    // if use_revComplement is true -> use data from complementary strand (if complement is set for gene)
974    //                    otherwise -> use data from primary strand (sort+merge parts by position)
975    //
976    // if partSeparator not is 0 -> insert partSeparator between single (non-merged) parts
977    //
978    // returns sequence as result (and length of sequence if 'gene_length' points to something)
979    //
980    // if 'pos_certain' contains '+', start behind position (or end at position)
981    //                           '-', start at position (or end before position)
982    //
983    // For zero-length genes (e.g. "711^712") this function returns an empty string.
984
985    GB_ERROR      error      = NULp;
986    char         *result     = NULp;
987    GBDATA       *gb_species = GB_get_grandfather(gb_gene);
988    GEN_position *pos        = GEN_read_position(gb_gene);
989
990    if (!pos) error = GB_await_error();
991    else {
992        GBDATA        *gb_seq        = GBT_find_sequence(gb_species, "ali_genom");
993        unsigned long  seq_length    = GB_read_count(gb_seq);
994        int            p;
995        int            parts         = pos->parts;
996        int            resultlen     = 0;
997        int            separatorSize = partSeparator ? 1 : 0;
998
999        init_gpp(parts);
1000
1001        // test positions and calculate overall result length
1002        for (p = 0; p<parts && !error; p++) {
1003            size_t start;
1004            size_t stop;
1005            getPartPositions(pos, p, &start, &stop);
1006
1007            if (start<1 || start>(stop+1) || stop > seq_length) { // do not reject zero-length genes (start == stop+1)
1008                error = GBS_global_string("Illegal gene position(s): start=%zu, end=%zu, seq.length=%li",
1009                                          start, stop, seq_length);
1010            }
1011            else {
1012                resultlen += stop-start+1;
1013            }
1014        }
1015
1016        if (separatorSize) resultlen += (parts-1)*separatorSize;
1017
1018        if (!error) {
1019            char T_or_U = 0;
1020            if (use_revComplement) {
1021                error = GBT_determine_T_or_U(GB_AT_DNA, &T_or_U, "reverse-complement");
1022            }
1023            else if (parts>1) {
1024                GEN_sortAndMergeLocationParts(pos);
1025                parts = pos->parts; // may have changed
1026            }
1027
1028            if (!error) {
1029                const char *seq_data = gb_cache_genome(gb_seq);
1030                char       *resultpos;
1031
1032                ARB_alloc(result, resultlen+1);
1033                resultpos = result;
1034
1035                if (gene_length) *gene_length = resultlen;
1036
1037                for (p = 0; p<parts; ++p) {
1038                    size_t start;
1039                    size_t stop;
1040                    getPartPositions(pos, p, &start, &stop);
1041
1042                    int len = stop-start+1;
1043
1044                    if (separatorSize && p>0) *resultpos++ = partSeparator;
1045
1046                    memcpy(resultpos, seq_data+start-1, len);
1047                    if (T_or_U && pos->complement[p]) {
1048                        GBT_reverseComplementNucSequence(resultpos, len, T_or_U);
1049                    }
1050                    resultpos += len;
1051                }
1052
1053                resultpos[0] = 0;
1054            }
1055        }
1056        GEN_free_position(pos);
1057    }
1058
1059    gb_assert(contradicted(result, error)); // either result or error!
1060
1061    if (error) {
1062        char *id = GEN_global_gene_identifier(gb_gene, gb_species);
1063        error    = GB_export_errorf("Can't read sequence of '%s' (Reason: %s)", id, error);
1064        free(id);
1065        free(result);
1066        result   = NULp;
1067    }
1068
1069    return result;
1070}
1071
1072char *GBT_read_gene_sequence(GBDATA *gb_gene, bool use_revComplement, char partSeparator) {
1073    return GBT_read_gene_sequence_and_length(gb_gene, use_revComplement, partSeparator, NULp);
1074}
1075
1076// --------------------------------------------------------------------------------
1077
1078#ifdef UNIT_TESTS
1079#include <test_unit.h>
1080
1081#define TEST_EXPECT_EXISTING_ALIGNMENTS(expected) do{           \
1082        names.erase();                                          \
1083        GBT_get_alignment_names(names, gb_main);                \
1084        TEST_EXPECT_STRARRAY_CONTAINS(names, '*', expected);    \
1085    }while(0)
1086
1087
1088#define TEST_EXPECT_EXISTING_ALIGNMENTS__BROKEN(want,got) do{           \
1089        names.erase();                                                  \
1090        GBT_get_alignment_names(names, gb_main);                        \
1091        TEST_EXPECT_STRARRAY_CONTAINS__BROKEN(names, '*', want,got);    \
1092    }while(0)
1093
1094
1095
1096#define TEST_EXPECT_DEFAULT_ALIGNMENT(expected)                                                         \
1097    TEST_EXPECT_EQUAL_STRINGCOPY__NOERROREXPORTED(GBT_get_default_alignment(gb_main), expected);
1098
1099
1100#define TEST_EXPECT_ALIGNMENT_STATE(expected_default,expected_existing) do{     \
1101        GB_initial_transaction ta(gb_main);                                     \
1102        TEST_EXPECT_DEFAULT_ALIGNMENT(expected_default);                        \
1103        TEST_EXPECT_EXISTING_ALIGNMENTS(expected_existing);                     \
1104    }while(0)
1105
1106void TEST_alignment() {
1107    GB_shell  shell;
1108    GBDATA   *gb_main = GB_open("TEST_prot.arb", "r"); // ../UNIT_TESTER/run/TEST_prot.arb
1109
1110    ConstStrArray  names;
1111    {
1112        GB_transaction ta(gb_main);
1113
1114        TEST_EXPECT_EQUAL(GBT_count_alignments(gb_main), 2);
1115
1116        TEST_EXPECT_DEFAULT_ALIGNMENT("ali_tuf_dna");
1117        TEST_EXPECT_EXISTING_ALIGNMENTS("ali_tuf_pro*ali_tuf_dna");
1118
1119        for (int i = 0; names[i]; ++i) {
1120            long len = GBT_get_alignment_len(gb_main, names[i]);
1121            TEST_EXPECT_EQUAL(len, i ? 1462 : 487);
1122
1123            char *type_name = GBT_get_alignment_type_string(gb_main, names[i]);
1124            TEST_EXPECT_EQUAL(type_name, i ? "dna" : "ami");
1125            free(type_name);
1126
1127            GB_alignment_type type = GBT_get_alignment_type(gb_main, names[i]);
1128            TEST_EXPECT_EQUAL(type, i ? GB_AT_DNA : GB_AT_AA);
1129            TEST_EXPECT_EQUAL(GBT_is_alignment_protein(gb_main, names[i]), !i);
1130        }
1131
1132        // test functions called with aliname == NULp
1133        TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GBT_get_alignment(gb_main, NULp), "no alignment");
1134        TEST_EXPECT_EQUAL(GBT_get_alignment_len(gb_main, NULp), -1);
1135        TEST_EXPECT_CONTAINS(GB_await_error(), "no alignment");
1136        TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GBT_get_alignment_type_string(gb_main, NULp), "no alignment");
1137
1138        // test functions called with aliname == NO_ALI_SELECTED
1139        TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GBT_get_alignment(gb_main, NO_ALI_SELECTED), "no alignment");
1140        TEST_EXPECT_EQUAL(GBT_get_alignment_len(gb_main, NO_ALI_SELECTED), -1);
1141        TEST_EXPECT_CONTAINS(GB_await_error(), "no alignment");
1142        TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GBT_get_alignment_type_string(gb_main, NO_ALI_SELECTED), "no alignment");
1143    }
1144
1145    // test copy, move(rename), delete alignments
1146    TEST_EXPECT_ALIGNMENT_STATE("ali_tuf_dna", "ali_tuf_pro*ali_tuf_dna");
1147
1148    // copy ali + test names:
1149    {
1150        GB_transaction ta(gb_main);
1151
1152        TEST_EXPECT_NO_ERROR(GBT_copy_alignment(gb_main, "ali_tuf_pro", "ali_copied_pro"));
1153        TEST_EXPECT_NO_ERROR(GBT_copy_alignment(gb_main, "ali_tuf_dna", "ali_copied_dna"));
1154    }
1155    TEST_EXPECT_ALIGNMENT_STATE("ali_tuf_dna", "ali_tuf_pro*ali_tuf_dna*ali_copied_pro*ali_copied_dna");
1156
1157    // rename ali + test names:
1158    {
1159        GB_transaction ta(gb_main);
1160        TEST_EXPECT_ERROR_CONTAINS(GBT_rename_alignment(gb_main, "ali_tuf_pro", "ali_moved_pro"), "Security error: deleting entry 'alignment' not permitted");
1161    }
1162    TEST_EXPECT_ALIGNMENT_STATE("ali_tuf_dna", "ali_tuf_pro*ali_tuf_dna*ali_copied_pro*ali_copied_dna");
1163
1164    {
1165        GB_transaction ta(gb_main);
1166        GB_securityLevel raise(gb_main, 6);
1167        TEST_EXPECT_NO_ERROR(GBT_rename_alignment(gb_main, "ali_tuf_pro", "ali_moved_pro"));
1168        TEST_EXPECT_NO_ERROR(GBT_rename_alignment(gb_main, "ali_tuf_dna", "ali_moved_dna"));
1169    }
1170    TEST_EXPECT_ALIGNMENT_STATE("ali_moved_dna", "ali_copied_pro*ali_copied_dna*ali_moved_pro*ali_moved_dna");
1171
1172    // delete ali + test names:
1173    {
1174        GB_transaction ta(gb_main);
1175        TEST_EXPECT_NO_ERROR(GBT_delete_alignment(gb_main, "ali_copied_pro"));
1176        TEST_EXPECT_NO_ERROR(GBT_delete_alignment(gb_main, "ali_moved_dna")); // @@@ the rename-test (above) lowered the security of 'ali_tuf_dna' (from 6 to 0?). unwanted behavior!
1177    }
1178    TEST_EXPECT_ALIGNMENT_STATE("ali_moved_dna", // @@@ unwanted behavior (deleted alignment is selected as default)
1179                                "ali_copied_dna*ali_moved_pro");
1180
1181    // trigger error cases:
1182    {
1183        GB_transaction ta(gb_main);
1184        TEST_EXPECT_ERROR_CONTAINS(GBT_rename_alignment(gb_main, "ali_copied_dna", "ali_moved_pro"), "destination alignment 'ali_moved_pro' already exists");
1185        TEST_EXPECT_ERROR_CONTAINS(GBT_copy_alignment(gb_main, "ali_copied_dna", "ali_copied_DNA"), "Cannot copy alignment if destination name only differs in case.");
1186    }
1187
1188    // test case-only rename
1189    {
1190        GB_transaction ta(gb_main);
1191        TEST_EXPECT_NO_ERROR(GBT_rename_alignment(gb_main, "ali_copied_dna", "ali_copied_DNA"));
1192    }
1193    TEST_EXPECT_ALIGNMENT_STATE("ali_copied_DNA", "ali_moved_pro*ali_copied_DNA");
1194
1195    GB_close(gb_main);
1196}
1197TEST_PUBLISH(TEST_alignment);
1198
1199#endif // UNIT_TESTS
Note: See TracBrowser for help on using the repository browser.