source: tags/ms_ra2q1/GENOM_IMPORT/DBwriter.cxx

Last change on this file was 16766, checked in by westram, 6 years ago
File size: 15.4 KB
Line 
1// ================================================================ //
2//                                                                  //
3//   File      : DBwriter.cxx                                       //
4//   Purpose   :                                                    //
5//                                                                  //
6//   Coded by Ralf Westram (coder@reallysoft.de) in November 2006   //
7//   Institute of Microbiology (Technical University Munich)        //
8//   http://www.arb-home.de/                                        //
9//                                                                  //
10// ================================================================ //
11
12#include "DBwriter.h"
13
14#define AW_RENAME_SKIP_GUI
15
16#include <algorithm>
17#include <AW_rename.hxx>
18#include <Translate.hxx>
19#include <aw_question.hxx>
20#include <GEN.hxx>
21#include <adGene.h>
22
23#define ARB_GENE_REF "ARB_is_gene"
24
25using namespace std;
26
27typedef SmartCustomPtr(GEN_position, GEN_free_position) GEN_positionPtr;
28
29// --------------------------------------------------------------------------------
30
31void DBerror::init(const string& msg, GB_ERROR gberror) {
32    gi_assert(gberror);
33    if (gberror) err = msg+" (Reason: "+gberror+")";
34    else err = msg; // ndebug!
35}
36
37DBerror::DBerror() { init("", GB_await_error()); }
38DBerror::DBerror(const char *msg) { string errmsg(msg); init(errmsg, GB_await_error()); }
39DBerror::DBerror(const string& msg) { init(msg, GB_await_error()); }
40DBerror::DBerror(const char *msg, GB_ERROR gberror) { string errmsg(msg); init(errmsg, gberror); }
41DBerror::DBerror(const string& msg, GB_ERROR gberror) { init(msg, gberror); }
42
43// --------------------------------------------------------------------------------
44// DB access functions, throwing DBerror on failure
45
46static GBDATA *DB_create_container(GBDATA *parent, const char *name, bool mark) {
47    // create container (optionally set mark flag)
48    GBDATA *gb_container = GB_create_container(parent, name);
49    if (!gb_container) throw DBerror(GBS_global_string("Failed to create container '%s'", name));
50
51    if (mark) GB_write_flag(gb_container, 1);
52
53    return gb_container;
54}
55
56static GBDATA *DB_create_string_field(GBDATA *parent, const char *field, const char *content) {
57    // create field with content
58
59    gi_assert(content[0]);
60    // do NOT WRITE empty string-fields into ARB DB,
61    // cause ARB DB does not differ between empty content and non-existing fields
62    // (i.e. when writing an empty string, ARB removes the field)
63
64    GBDATA *gb_field = GB_create(parent, field, GB_STRING);
65    if (!gb_field) throw DBerror(GBS_global_string("Failed to create field '%s'", field));
66
67    GB_ERROR err = GB_write_string(gb_field, content);
68    if (err) throw DBerror(GBS_global_string("Failed to write to field '%s'", field), err);
69
70    return gb_field;
71}
72
73static GBDATA *DB_create_byte_field(GBDATA *parent, const char *field, unsigned char content) {
74    // create field with content
75    GBDATA *gb_field = GB_create(parent, field, GB_BYTE);
76    if (!gb_field) throw DBerror(GBS_global_string("Failed to create field '%s'", field));
77
78    GB_ERROR err = GB_write_byte(gb_field, content);
79    if (err) throw DBerror(GBS_global_string("Failed to write to field '%s'", field), err);
80
81    return gb_field;
82}
83
84// --------------------------------------------------------------------------------
85
86void DBwriter::createOrganism(const string& flatfile, const char *importerTag) {
87    gi_assert(!gb_organism && !gb_gene_data);
88
89    // create the organism
90    {
91        UniqueNameDetector& UND_species = *session.und_species;
92
93        char *organism_name = AWTC_makeUniqueShortName("genome", UND_species);
94        if (!organism_name) throw DBerror();
95
96        gb_organism = DB_create_container(session.gb_species_data, "species", true);
97        DB_create_string_field(gb_organism, "name", organism_name);
98
99        UND_species.add_name(organism_name);
100        free(organism_name);
101    }
102
103    // store info about source
104    DB_create_string_field(gb_organism, "ARB_imported_from", flatfile.c_str());
105    DB_create_string_field(gb_organism, "ARB_imported_format", importerTag);
106}
107
108class NoCaseCmp {
109    static bool less_nocase(char c1, char c2) { return toupper(c1) < toupper(c2); }
110    static bool nonequal_nocase(char c1, char c2) { return toupper(c1) != toupper(c2); }
111public:
112    NoCaseCmp() {}
113
114    bool operator()(const string& s1, const string& s2) const {
115        return lexicographical_compare(s1.begin(), s1.end(),
116                                       s2.begin(), s2.end(),
117                                       less_nocase);
118    }
119
120    static bool has_prefix(const string& s1, const string& s2) {
121        // return true if s1 starts with prefix s2
122        return
123            s1.length() >= s2.length() &&
124            !lexicographical_compare(s1.begin(), s1.begin()+s2.length(),
125                                     s2.begin(), s2.end(),
126                                     nonequal_nocase);
127    }
128};
129
130typedef map<string, string, NoCaseCmp> TranslateMap;
131struct Translator { TranslateMap trans; };
132
133Translator *DBwriter::unreserve = NULp;
134
135const string& DBwriter::getUnreservedQualifier(const string& qualifier) {
136    // return a non-reserved qualifier
137    // (some are reserved - e.g. name, pos_start, ... and all starting with 'ARB_')
138    // if a qualifier is reserved, 'ORG_' is prepended.
139    //
140    // (Note: When we'll export data, 'ORG_' shall be removed from qualifiers!)
141
142    static string prefix = "ORG_";
143
144    if (!unreserve) {
145        unreserve = new Translator;
146        const char *reserved[] = {
147            "name", "type",
148            "pos_start", "pos_stop", "pos_complement", "pos_certain", "pos_joined",
149            NULp
150        };
151
152        for (int i = 0; reserved[i]; ++i) {
153            unreserve->trans[reserved[i]] = prefix+reserved[i];
154        }
155    }
156
157    TranslateMap::const_iterator found = unreserve->trans.find(qualifier);
158    if (found != unreserve->trans.end()) { // qualifier is reserved
159        return found->second;
160    }
161    if (NoCaseCmp::has_prefix(qualifier, prefix) || // qualifier starts with 'ORG_'
162        NoCaseCmp::has_prefix(qualifier, "ARB_")) // or 'ARB_'
163    {
164        unreserve->trans[qualifier] = prefix+qualifier; // add as 'ORG_ORG_' or 'ORG_ARB_' to TranslateMap
165        return unreserve->trans[qualifier];
166    }
167    return qualifier;
168}
169
170void DBwriter::writeFeature(const Feature& feature, long seqLength) {
171    gi_assert(gb_organism);
172    if (!gb_gene_data) {
173        gb_gene_data = DB_create_container(gb_organism, "gene_data", false);
174        generatedGenes.clear();
175    }
176
177    // create new gene
178    GBDATA *gb_gene;
179    {
180        string                gene_name = feature.createGeneName();
181        NameCounter::iterator existing  = generatedGenes.find(gene_name);
182        if (existing == generatedGenes.end()) { // first occurrence of that gene name
183            generatedGenes[gene_name] = 1;
184        }
185        else {
186            existing->second++; // increment occurrences
187        }
188
189        gb_gene = DB_create_container(gb_gene_data, "gene", false);
190        DB_create_string_field(gb_gene, "name", gene_name.c_str());
191
192        string type = feature.getType();
193        DB_create_string_field(gb_gene, "type", type.c_str());
194
195        if (type == "source") {
196            DB_create_byte_field(gb_gene, ARB_HIDDEN, 1);
197        }
198    }
199
200    // store location
201    {
202        GEN_positionPtr pos = feature.getLocation().create_GEN_position();
203        GB_ERROR        err = GEN_write_position(gb_gene, &*pos, seqLength);
204        if (err) throw DBerror("Failed to write location", err);
205    }
206
207    // store qualifiers
208    {
209        const stringMap& qualifiers = feature.getQualifiers();
210        stringMapCIter   e          = qualifiers.end();
211
212        for (stringMapCIter i = qualifiers.begin(); i != e; ++i) {
213            const string& unreserved = getUnreservedQualifier(i->first);
214            DB_create_string_field(gb_gene, unreserved.c_str(), i->second.c_str());
215        }
216    }
217}
218
219void DBwriter::writeSequence(const SequenceBuffer& seqData) {
220    gi_assert(gb_organism);
221    GBDATA *gb_data = GBT_add_data(gb_organism, ali_name, "data", GB_STRING);
222    if (!gb_data) throw DBerror("Failed to create alignment");
223
224    GB_ERROR err = GB_write_string(gb_data, seqData.getSequence());
225    if (err) throw DBerror("Failed to write alignment", err);
226}
227
228void DBwriter::renumberDuplicateGenes() {
229    NameCounter renameCounter; // for those genes which get renumbered, count upwards here
230    {
231        NameCounter::iterator gg_end = generatedGenes.end();
232        for (NameCounter::iterator gg = generatedGenes.begin(); gg != gg_end; ++gg) {
233            if (gg->second > 1) renameCounter[gg->first] = 0;
234        }
235    }
236    NameCounter::iterator rc_end = renameCounter.end();
237
238    for (GBDATA *gb_gene = GB_entry(gb_gene_data, "gene");
239         gb_gene;
240         gb_gene = GB_nextEntry(gb_gene))
241    {
242        GBDATA *gb_name   = GB_entry(gb_gene, "name");
243        string  gene_name = GB_read_char_pntr(gb_name);
244
245        NameCounter::iterator rc = renameCounter.find(gene_name);
246        if (rc != rc_end) { // rename current_gene
247            int maxOccurrences = generatedGenes[gene_name];
248            rc->second++;   // increment occurrence counter
249            gi_assert(rc->second <= maxOccurrences);
250
251            int digits = strlen(GBS_global_string("%i", maxOccurrences));
252            gene_name    += GBS_global_string("_%0*i", digits, rc->second);
253            GB_ERROR err  = GB_write_string(gb_name, gene_name.c_str());
254            if (err) throw DBerror("Failed to write to field 'name' (during gene-renumbering)", err);
255        }
256    }
257}
258
259static void importerWarning(AW_CL cl_importer, const char *message) {
260    Importer *importer = reinterpret_cast<Importer*>(cl_importer);
261    importer->warning(message);
262}
263
264void DBwriter::testAndRemoveTranslations(Importer& importer) {
265    GEN_testAndRemoveTranslations(gb_gene_data, importerWarning, reinterpret_cast<AW_CL>(&importer), session.ok_to_ignore_wrong_start_codon);
266}
267
268// ----------------------------------------------
269//      hide duplicated genes (from genebank)
270
271inline bool operator<(const GEN_positionPtr& A, const GEN_positionPtr& B) {
272    const GEN_position& a = *A;
273    const GEN_position& b = *B;
274
275    int cmp = int(a.start_pos[0]) - int(b.start_pos[0]);
276    if (!cmp) {
277        cmp = int(a.stop_pos[a.parts-1]) - int(b.stop_pos[b.parts-1]);
278        if (!cmp) {
279            cmp = int(a.parts) - int(b.parts); // less parts is <
280            if (!cmp) {
281                for (int p = 0; p<a.parts; ++p) { // compare all parts
282                    cmp = int(b.complement[p]) - int(a.complement[p]); if (cmp) break; // non-complement is <
283                    cmp = int(a.start_pos[p])  - int(b.start_pos[p]);  if (cmp) break;
284                    cmp = int(a.stop_pos[p])   - int(b.stop_pos[p]);   if (cmp) break;
285                }
286            }
287        }
288    }
289
290    return cmp<0;
291}
292
293class PosGene {
294    RefPtr<GBDATA>          gb_Gene;
295    mutable GEN_positionPtr pos;
296
297public:
298    PosGene(GBDATA *gb_gene) : gb_Gene(gb_gene) {
299        GEN_position *pp = GEN_read_position(gb_gene);
300        if (!pp) {
301            throw GBS_global_string("Can't read gene position (Reason: %s)", GB_await_error());
302        }
303        pos = pp;
304    }
305
306    const GEN_positionPtr& getPosition() const { return pos; }
307
308    const char *getName() const {
309        GBDATA *gb_name = GB_entry(gb_Gene, "name");
310        gi_assert(gb_name);
311        return GB_read_char_pntr(gb_name);
312    }
313    const char *getType() const {
314        GBDATA *gb_type = GB_entry(gb_Gene, "type");
315        gi_assert(gb_type);
316        return GB_read_char_pntr(gb_type);
317    }
318    bool hasType(const char *type) const { return strcmp(getType(), type) == 0; }
319
320    void hide() { DB_create_byte_field(gb_Gene, ARB_HIDDEN, 1); }
321    void addRefToGene(const char *name_of_gene) { DB_create_string_field(gb_Gene, ARB_GENE_REF, name_of_gene); }
322
323#if defined(DEBUG)
324    const char *description() const {
325        return GBS_global_string("%zi-%zi (%i parts)", pos->start_pos[0], pos->stop_pos[pos->parts-1], pos->parts);
326    }
327    void dump() {
328        GB_dump(gb_Gene);
329    }
330#endif // DEBUG
331
332};
333
334inline bool operator<(const PosGene& a, const PosGene& b) {
335    return a.getPosition() < b.getPosition();
336}
337inline bool operator == (const PosGene& a, const PosGene& b) {
338    return !(a<b || b<a);
339}
340
341class hasType {
342    const char *type;
343public:
344    hasType(const char *t) : type(t) {}
345    bool operator()(const PosGene& pg) { return pg.hasType(type); }
346};
347
348void DBwriter::hideUnwantedGenes() {
349    typedef vector<PosGene> Genes;
350    typedef Genes::iterator GeneIter;
351
352    Genes gps;
353
354    // read all gene positions
355    for (GBDATA *gb_gene = GB_entry(gb_gene_data, "gene"); gb_gene; gb_gene = GB_nextEntry(gb_gene)) {
356        gps.push_back(PosGene(gb_gene));
357    }
358
359    sort(gps.begin(), gps.end()); // sort positions
360
361    // find duplicate geness (with identical location)
362    GeneIter end        = gps.end();
363    GeneIter p          = gps.begin();
364    GeneIter firstEqual = p;
365    GeneIter lastEqual  = p;
366
367    while (firstEqual != end) {
368        ++p;
369        if (p != end && *p == *firstEqual) {
370            lastEqual = p;
371        }
372        else {                  // found different element (or last)
373            int count = lastEqual-firstEqual+1;
374
375            if (count>1) { // we have 2 or more duplicate genes
376                GeneIter equalEnd = lastEqual+1;
377                GeneIter gene     = find_if(firstEqual, equalEnd, hasType("gene")); // locate 'type' == 'gene'
378
379                if (gene != equalEnd) { // found type gene
380                    bool        hideGene  = false;
381                    GeneIter    e         = firstEqual;
382                    const char *gene_name = gene->getName();
383
384                    while (e != equalEnd) {
385                        if (e != gene) { // for all genes that have 'type' != 'gene'
386                            e->addRefToGene(gene_name); // add a reference to the gene
387                            hideGene = true; // and hide the gene
388                        }
389                        ++e;
390                    }
391
392                    if (hideGene) gene->hide();
393                }
394            }
395            firstEqual = lastEqual = p;
396        }
397    }
398}
399
400
401void DBwriter::finalizeOrganism(const MetaInfo& meta, const References& refs, Importer& importer) {
402    // write metadata
403    {
404        const stringMap& entries = meta.getEntries();
405        stringMapCIter   e       = entries.end();
406
407        for (stringMapCIter i = entries.begin(); i != e; ++i) {
408            const string& content = i->second;
409            if (!content.empty()) { // skip empty entries
410                DB_create_string_field(gb_organism, i->first.c_str(), content.c_str());
411            }
412        }
413    }
414
415    // write references
416    {
417        stringSet refKeys;
418        refs.getKeys(refKeys);
419
420        stringSetIter e = refKeys.end();
421        for (stringSetIter i = refKeys.begin(); i != e; ++i) {
422            DB_create_string_field(gb_organism, i->c_str(), refs.tagged_content(*i).c_str());
423        }
424    }
425
426    // finalize genes data
427    if (gb_gene_data) {
428        renumberDuplicateGenes();            // renumber genes with equal names
429        testAndRemoveTranslations(importer); // test translations and remove reproducible translations
430        hideUnwantedGenes();
431    }
432    else GB_warning("No genes have been written (missing feature table?)");
433
434    // cleanup
435    generatedGenes.clear();
436    gb_gene_data = NULp;
437    gb_organism  = NULp;
438}
439
440void DBwriter::deleteStaticData() {
441    if (unreserve) {
442        delete unreserve;
443        unreserve = NULp;
444    }
445}
446
447
448
Note: See TracBrowser for help on using the repository browser.