in progress
[jalview.git] / forester / java / src / org / forester / ws / seqdb / SequenceDbWsTools.java
1 // $Id:
2 // forester -- software libraries and applications
3 // for genomics and evolutionary biology research.
4 //
5 // Copyright (C) 2010 Christian M Zmasek
6 // Copyright (C) 2010 Sanford-Burnham Medical Research Institute
7 // All rights reserved
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 //
23 // Contact: phylosoft @ gmail . com
24 // WWW: https://sites.google.com/site/cmzmasek/home/software/forester
25
26 package org.forester.ws.seqdb;
27
28 import java.io.BufferedReader;
29 import java.io.FileNotFoundException;
30 import java.io.IOException;
31 import java.io.InputStreamReader;
32 import java.io.UnsupportedEncodingException;
33 import java.net.URL;
34 import java.net.URLConnection;
35 import java.net.URLEncoder;
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.SortedSet;
39 import java.util.TreeSet;
40
41 import org.forester.go.GoTerm;
42 import org.forester.io.parsers.phyloxml.PhyloXmlDataFormatException;
43 import org.forester.phylogeny.Phylogeny;
44 import org.forester.phylogeny.PhylogenyNode;
45 import org.forester.phylogeny.data.Accession;
46 import org.forester.phylogeny.data.Accession.Source;
47 import org.forester.phylogeny.data.Annotation;
48 import org.forester.phylogeny.data.Identifier;
49 import org.forester.phylogeny.data.Sequence;
50 import org.forester.phylogeny.data.Taxonomy;
51 import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
52 import org.forester.sequence.MolecularSequence.TYPE;
53 import org.forester.util.ForesterUtil;
54 import org.forester.util.SequenceAccessionTools;
55
56 public final class SequenceDbWsTools {
57
58     public final static String   BASE_UNIPROT_URL        = "http://www.uniprot.org/";
59     public final static int      DEFAULT_LINES_TO_RETURN = 4000;
60     public final static String   EMBL_DBS_REFSEQ_N       = "refseqn";
61     public final static String   EMBL_DBS_REFSEQ_P       = "refseqp";
62     public final static String   EMBL_GENBANK            = "http://www.ebi.ac.uk/Tools/dbfetch/dbfetch?db=GENBANK&style=raw&id=";
63     public final static String   EMBL_REFSEQ             = "http://www.ebi.ac.uk/Tools/dbfetch/dbfetch?db=REFSEQ&style=raw&id=";
64     public final static String   EMBL_EMBL               = "http://www.ebi.ac.uk/Tools/dbfetch/dbfetch?db=EMBL&style=raw&id=";
65     private final static boolean DEBUG                   = true;
66     private final static String  URL_ENC                 = "UTF-8";
67     private final static int     SLEEP                   = 200;
68
69     public static List<UniProtTaxonomy> getTaxonomiesFromCommonNameStrict( final String cn,
70                                                                            final int max_taxonomies_return )
71             throws IOException {
72         final List<UniProtTaxonomy> taxonomies = getTaxonomiesFromCommonName( cn, max_taxonomies_return );
73         if ( ( taxonomies != null ) && ( taxonomies.size() > 0 ) ) {
74             final List<UniProtTaxonomy> filtered_taxonomies = new ArrayList<UniProtTaxonomy>();
75             for( final UniProtTaxonomy taxonomy : taxonomies ) {
76                 if ( taxonomy.getCommonName().equalsIgnoreCase( cn ) ) {
77                     filtered_taxonomies.add( taxonomy );
78                 }
79             }
80             return filtered_taxonomies;
81         }
82         return null;
83     }
84
85     public static List<UniProtTaxonomy> getTaxonomiesFromId( final String id, final int max_taxonomies_return )
86             throws IOException {
87         final List<String> result = getTaxonomyStringFromId( id, max_taxonomies_return );
88         if ( result.size() > 0 ) {
89             return parseUniProtTaxonomy( result );
90         }
91         return null;
92     }
93
94     /**
95      * Does not return "sub-types".
96      * For example, for "Mus musculus" only returns "Mus musculus"
97      * and not "Mus musculus", "Mus musculus bactrianus", ...
98      * 
99      */
100     public static List<UniProtTaxonomy> getTaxonomiesFromScientificNameStrict( final String sn,
101                                                                                final int max_taxonomies_return )
102             throws IOException {
103         final List<UniProtTaxonomy> taxonomies = getTaxonomiesFromScientificName( sn, max_taxonomies_return );
104         if ( ( taxonomies != null ) && ( taxonomies.size() > 0 ) ) {
105             final List<UniProtTaxonomy> filtered_taxonomies = new ArrayList<UniProtTaxonomy>();
106             for( final UniProtTaxonomy taxonomy : taxonomies ) {
107                 if ( taxonomy.getScientificName().equalsIgnoreCase( sn ) ) {
108                     filtered_taxonomies.add( taxonomy );
109                 }
110             }
111             return filtered_taxonomies;
112         }
113         return null;
114     }
115
116     public static List<UniProtTaxonomy> getTaxonomiesFromTaxonomyCode( final String code,
117                                                                        final int max_taxonomies_return )
118             throws IOException {
119         final String my_code = new String( code );
120         final List<String> result = getTaxonomyStringFromTaxonomyCode( my_code, max_taxonomies_return );
121         if ( result.size() > 0 ) {
122             return parseUniProtTaxonomy( result );
123         }
124         return null;
125     }
126
127     public static SequenceDatabaseEntry obtainEmblEntry( final Accession acc ) throws IOException {
128         return obtainEmblEntry( acc, DEFAULT_LINES_TO_RETURN );
129     }
130
131     public static SequenceDatabaseEntry obtainEmblEntry( final Accession acc, final int max_lines_to_return )
132             throws IOException {
133         final List<String> lines = queryEmblDb( acc, max_lines_to_return );
134         return EbiDbEntry.createInstanceFromPlainTextForRefSeq( lines );
135     }
136
137     public static SequenceDatabaseEntry obtainEntry( final String acc_str ) throws IOException {
138         if ( ForesterUtil.isEmpty( acc_str ) ) {
139             throw new IllegalArgumentException( "cannot not extract sequence db accessor from null or empty string" );
140         }
141         final Accession acc = SequenceAccessionTools.parseAccessorFromString( acc_str );
142         if ( acc == null ) {
143             throw new IllegalArgumentException( "could not extract acceptable sequence db accessor from \"" + acc_str
144                     + "\"" );
145         }
146         if ( acc.getSource().equals( Source.REFSEQ.toString() ) || acc.getSource().equals( Source.EMBL.toString() )
147                 || acc.getSource().equals( Source.NCBI.toString() ) ) {
148             return obtainEmblEntry( acc, DEFAULT_LINES_TO_RETURN );
149         }
150         else if ( acc.getSource().equals( Source.UNIPROT.toString() ) ) {
151             return obtainUniProtEntry( acc.getValue(), DEFAULT_LINES_TO_RETURN );
152         }
153         else {
154             throw new IllegalArgumentException( "don't know how to handle request for source \"" + acc.getSource()
155                     + "\"" );
156         }
157     }
158
159     public static SequenceDatabaseEntry obtainRefSeqEntryFromEmbl( final Accession acc ) throws IOException {
160         return obtainRefSeqEntryFromEmbl( acc, DEFAULT_LINES_TO_RETURN );
161     }
162
163     public static SequenceDatabaseEntry obtainRefSeqEntryFromEmbl( final Accession acc, final int max_lines_to_return )
164             throws IOException {
165         final List<String> lines = queryEmblDbForRefSeqEntry( acc, max_lines_to_return );
166         return EbiDbEntry.createInstanceFromPlainTextForRefSeq( lines );
167     }
168
169     public final static Accession obtainSeqAccession( final PhylogenyNode node ) {
170         Accession acc = SequenceAccessionTools.obtainFromSeqAccession( node );
171         if ( !isAccessionAcceptable( acc ) ) {
172             acc = SequenceAccessionTools.obtainAccessorFromDataFields( node );
173         }
174         return acc;
175     }
176
177     public final static void obtainSeqInformation( final boolean allow_to_set_taxonomic_data,
178                                                    final int lines_to_return,
179                                                    final SortedSet<String> not_found,
180                                                    final PhylogenyNode node ) throws IOException {
181         final Accession acc = obtainSeqAccession( node );
182         if ( !isAccessionAcceptable( acc ) ) {
183             if ( node.isExternal() || !node.isEmpty() ) {
184                 not_found.add( node.toString() );
185             }
186         }
187         else {
188             addDataFromDbToNode( allow_to_set_taxonomic_data, lines_to_return, not_found, node, acc );
189         }
190     }
191
192     public final static void obtainSeqInformation( final boolean allow_to_set_taxonomic_data,
193                                                    final SortedSet<String> not_found,
194                                                    final PhylogenyNode node ) throws IOException {
195         obtainSeqInformation( allow_to_set_taxonomic_data, DEFAULT_LINES_TO_RETURN, not_found, node );
196     }
197
198     public final static SortedSet<String> obtainSeqInformation( final Phylogeny phy,
199                                                                 final boolean ext_nodes_only,
200                                                                 final boolean allow_to_set_taxonomic_data,
201                                                                 final int lines_to_return ) throws IOException {
202         final SortedSet<String> not_found = new TreeSet<String>();
203         for( final PhylogenyNodeIterator iter = phy.iteratorPostorder(); iter.hasNext(); ) {
204             final PhylogenyNode node = iter.next();
205             if ( node.isExternal() || !ext_nodes_only ) {
206                 obtainSeqInformation( allow_to_set_taxonomic_data, lines_to_return, not_found, node );
207             }
208         }
209         return not_found;
210     }
211
212     public final static void obtainSeqInformation( final PhylogenyNode node ) throws IOException {
213         obtainSeqInformation( true, DEFAULT_LINES_TO_RETURN, new TreeSet<String>(), node );
214     }
215
216     public static SequenceDatabaseEntry obtainUniProtEntry( final String query ) throws IOException {
217         return obtainUniProtEntry( query, DEFAULT_LINES_TO_RETURN );
218     }
219
220     public static SequenceDatabaseEntry obtainUniProtEntry( final String query, final int max_lines_to_return )
221             throws IOException {
222         final List<String> lines = queryUniprot( "uniprot/" + query + ".txt", max_lines_to_return );
223         return UniProtEntry.createInstanceFromPlainText( lines );
224     }
225
226     public static List<String> queryDb( final String query, int max_lines_to_return, final String base_url )
227             throws IOException {
228         if ( ForesterUtil.isEmpty( query ) ) {
229             throw new IllegalArgumentException( "illegal attempt to use empty query " );
230         }
231         if ( max_lines_to_return < 1 ) {
232             max_lines_to_return = 1;
233         }
234         final URL url = new URL( base_url + query );
235         if ( DEBUG ) {
236             System.out.println( "url: " + url.toString() );
237         }
238         final URLConnection urlc = url.openConnection();
239         final BufferedReader in = new BufferedReader( new InputStreamReader( urlc.getInputStream() ) );
240         String line;
241         final List<String> result = new ArrayList<String>();
242         while ( ( line = in.readLine() ) != null ) {
243             if ( DEBUG ) {
244                 System.out.println( line );
245             }
246             result.add( line );
247             if ( result.size() > max_lines_to_return ) {
248                 break;
249             }
250         }
251         in.close();
252         try {
253             // To prevent accessing online dbs in too quick succession. 
254             Thread.sleep( SLEEP );
255         }
256         catch ( final InterruptedException e ) {
257             e.printStackTrace();
258         }
259         return result;
260     }
261
262     public static List<String> queryEmblDb( final Accession acc, final int max_lines_to_return ) throws IOException {
263         final StringBuilder url_sb = new StringBuilder();
264         //  url_sb.append( BASE_EMBL_DB_URL );
265         System.out.println( "source: " + acc.getSource() );
266         if ( acc.getSource().equals( Source.NCBI.toString() ) ) {
267             url_sb.append( EMBL_GENBANK );
268             //url_sb.append( '/' );
269         }
270         else if ( acc.getSource().equals( Source.REFSEQ.toString() ) ) {
271             url_sb.append( EMBL_REFSEQ );
272         }
273         else if ( acc.getSource().equals( Source.EMBL.toString() ) ) {
274             url_sb.append( EMBL_EMBL );
275         }
276         else {
277             throw new IllegalArgumentException( "unable to handle source: " + acc.getSource() );
278         }
279         return queryDb( acc.getValue(), max_lines_to_return, url_sb.toString() );
280     }
281
282     public static List<String> queryEmblDbForRefSeqEntry( final Accession id, final int max_lines_to_return )
283             throws IOException {
284         final StringBuilder url_sb = new StringBuilder();
285         url_sb.append( EMBL_REFSEQ );
286         return queryDb( id.getValue(), max_lines_to_return, url_sb.toString() );
287     }
288
289     public static List<String> queryUniprot( final String query, final int max_lines_to_return ) throws IOException {
290         return queryDb( query, max_lines_to_return, BASE_UNIPROT_URL );
291     }
292
293     final static String extractFrom( final String target, final String a ) {
294         final int i_a = target.indexOf( a );
295         return target.substring( i_a + a.length() ).trim();
296     }
297
298     final static String extractFromTo( final String target, final String a, final String b ) {
299         final int i_a = target.indexOf( a );
300         final int i_b = target.indexOf( b );
301         if ( ( i_a < 0 ) || ( i_b < i_a ) ) {
302             throw new IllegalArgumentException( "attempt to extract from \"" + target + "\" between \"" + a
303                     + "\" and \"" + b + "\"" );
304         }
305         return target.substring( i_a + a.length(), i_b ).trim();
306     }
307
308     final static String extractTo( final String target, final String b ) {
309         final int i_b = target.indexOf( b );
310         return target.substring( 0, i_b ).trim();
311     }
312
313     private static void addDataFromDbToNode( final boolean allow_to_set_taxonomic_data,
314                                              final int lines_to_return,
315                                              final SortedSet<String> not_found,
316                                              final PhylogenyNode node,
317                                              final Accession acc ) throws IOException {
318         SequenceDatabaseEntry db_entry = null;
319         final String query = acc.getValue();
320         if ( acc.getSource().equals( Source.UNIPROT.toString() ) ) {
321             if ( DEBUG ) {
322                 System.out.println( "uniprot: " + query );
323             }
324             try {
325                 db_entry = obtainUniProtEntry( query, lines_to_return );
326             }
327             catch ( final FileNotFoundException e ) {
328                 // Eat this, and move to next.
329             }
330         }
331         else if ( acc.getSource().equals( Source.REFSEQ.toString() ) ) {
332             if ( DEBUG ) {
333                 System.out.println( "refseq: " + query );
334             }
335             try {
336                 db_entry = obtainRefSeqEntryFromEmbl( new Accession( query ), lines_to_return );
337             }
338             catch ( final FileNotFoundException e ) {
339                 // Eat this, and move to next.
340             }
341         }
342         else if ( acc.getSource().equals( Source.EMBL.toString() ) || acc.getSource().equals( Source.NCBI.toString() )
343                 || acc.getSource().equals( Source.EMBL.toString() ) ) {
344             if ( DEBUG ) {
345                 System.out.println( acc.toString() );
346             }
347             try {
348                 db_entry = obtainEmblEntry( acc, lines_to_return );
349             }
350             catch ( final FileNotFoundException e ) {
351                 // Eat this, and move to next.
352             }
353         }
354         else if ( acc.getSource().equals( Source.GI.toString() ) ) {
355             if ( DEBUG ) {
356                 System.out.println( "gi: " + query );
357             }
358             try {
359                 db_entry = obtainRefSeqEntryFromEmbl( new Accession( query ), lines_to_return );
360             }
361             catch ( final FileNotFoundException e ) {
362                 // Eat this, and move to next.
363             }
364         }
365         if ( ( db_entry != null ) && !db_entry.isEmpty() ) {
366             final Sequence seq = node.getNodeData().isHasSequence() ? node.getNodeData().getSequence() : new Sequence();
367             if ( !ForesterUtil.isEmpty( db_entry.getAccession() ) ) {
368                 seq.setAccession( new Accession( db_entry.getAccession(), acc.getSource() ) );
369             }
370             if ( !ForesterUtil.isEmpty( db_entry.getSequenceName() ) ) {
371                 seq.setName( db_entry.getSequenceName() );
372             }
373             if ( !ForesterUtil.isEmpty( db_entry.getGeneName() ) ) {
374                 seq.setGeneName( db_entry.getGeneName() );
375             }
376             if ( !ForesterUtil.isEmpty( db_entry.getSequenceSymbol() ) ) {
377                 try {
378                     seq.setSymbol( db_entry.getSequenceSymbol() );
379                 }
380                 catch ( final PhyloXmlDataFormatException e ) {
381                     // Eat this exception.
382                 }
383             }
384             if ( ( db_entry.getMolecularSequence() != null )
385                     && !ForesterUtil.isEmpty( db_entry.getMolecularSequence().getMolecularSequenceAsString() )
386                     && seq.getMolecularSequence().isEmpty() ) {
387                 seq.setMolecularSequence( db_entry.getMolecularSequence().getMolecularSequenceAsString() );
388                 seq.setMolecularSequenceAligned( false );
389                 if ( db_entry.getMolecularSequence().getType() == TYPE.AA ) {
390                     seq.setType( "protein" );
391                 }
392                 else if ( db_entry.getMolecularSequence().getType() == TYPE.DNA ) {
393                     seq.setType( "dna" );
394                 }
395                 else if ( db_entry.getMolecularSequence().getType() == TYPE.RNA ) {
396                     seq.setType( "rna" );
397                 }
398             }
399             if ( ( db_entry.getGoTerms() != null ) && !db_entry.getGoTerms().isEmpty() ) {
400                 for( final GoTerm go : db_entry.getGoTerms() ) {
401                     final Annotation ann = new Annotation( go.getGoId().getId() );
402                     ann.setDesc( go.getName() );
403                     seq.addAnnotation( ann );
404                 }
405             }
406             if ( ( db_entry.getCrossReferences() != null ) && !db_entry.getCrossReferences().isEmpty() ) {
407                 for( final Accession x : db_entry.getCrossReferences() ) {
408                     seq.addCrossReference( x );
409                 }
410             }
411             if ( !ForesterUtil.isEmpty( db_entry.getChromosome() ) && !ForesterUtil.isEmpty( db_entry.getMap() ) ) {
412                 seq.setLocation( "chr " + db_entry.getChromosome() + ", " + db_entry.getMap() );
413             }
414             else if ( !ForesterUtil.isEmpty( db_entry.getChromosome() ) ) {
415                 seq.setLocation( "chr " + db_entry.getChromosome() );
416             }
417             else if ( !ForesterUtil.isEmpty( db_entry.getMap() ) ) {
418                 seq.setLocation( db_entry.getMap() );
419             }
420             final Taxonomy tax = node.getNodeData().isHasTaxonomy() ? node.getNodeData().getTaxonomy() : new Taxonomy();
421             if ( !ForesterUtil.isEmpty( db_entry.getTaxonomyScientificName() ) ) {
422                 tax.setScientificName( db_entry.getTaxonomyScientificName() );
423             }
424             if ( allow_to_set_taxonomic_data && !ForesterUtil.isEmpty( db_entry.getTaxonomyIdentifier() ) ) {
425                 tax.setIdentifier( new Identifier( db_entry.getTaxonomyIdentifier(), "uniprot" ) );
426             }
427             node.getNodeData().setTaxonomy( tax );
428             node.getNodeData().setSequence( seq );
429         }
430         else {
431             if ( node.isExternal() || !node.isEmpty() ) {
432                 not_found.add( node.toString() );
433             }
434         }
435         try {
436             Thread.sleep( SLEEP );
437         }
438         catch ( final InterruptedException ie ) {
439         }
440     }
441
442     private static String encode( final String str ) throws UnsupportedEncodingException {
443         return URLEncoder.encode( str.trim(), URL_ENC );
444     }
445
446     private static List<UniProtTaxonomy> getTaxonomiesFromCommonName( final String cn, final int max_taxonomies_return )
447             throws IOException {
448         final List<String> result = getTaxonomyStringFromCommonName( cn, max_taxonomies_return );
449         if ( result.size() > 0 ) {
450             return parseUniProtTaxonomy( result );
451         }
452         return null;
453     }
454
455     private static List<UniProtTaxonomy> getTaxonomiesFromScientificName( final String sn,
456                                                                           final int max_taxonomies_return )
457             throws IOException {
458         final List<String> result = getTaxonomyStringFromScientificName( sn, max_taxonomies_return );
459         if ( result.size() > 0 ) {
460             return parseUniProtTaxonomy( result );
461         }
462         return null;
463     }
464
465     private static List<String> getTaxonomyStringFromCommonName( final String cn, final int max_lines_to_return )
466             throws IOException {
467         return queryUniprot( "taxonomy/?query=common%3a%22" + encode( cn ) + "%22&format=tab", max_lines_to_return );
468     }
469
470     private static List<String> getTaxonomyStringFromId( final String id, final int max_lines_to_return )
471             throws IOException {
472         return queryUniprot( "taxonomy/?query=id%3a%22" + encode( id ) + "%22&format=tab", max_lines_to_return );
473     }
474
475     private static List<String> getTaxonomyStringFromScientificName( final String sn, final int max_lines_to_return )
476             throws IOException {
477         return queryUniprot( "taxonomy/?query=scientific%3a%22" + encode( sn ) + "%22&format=tab", max_lines_to_return );
478     }
479
480     private static List<String> getTaxonomyStringFromTaxonomyCode( final String code, final int max_lines_to_return )
481             throws IOException {
482         return queryUniprot( "taxonomy/?query=mnemonic%3a%22" + encode( code ) + "%22&format=tab", max_lines_to_return );
483     }
484
485     private final static boolean isAccessionAcceptable( final Accession acc ) {
486         return ( !( ( acc == null ) || ForesterUtil.isEmpty( acc.getSource() ) || ForesterUtil.isEmpty( acc.getValue() ) || ( ( acc
487                 .getSource().equals( Source.UNIPROT.toString() ) )
488                 && ( acc.getSource().toString().equals( Source.EMBL.toString() ) ) && ( acc.getSource().toString()
489                 .equals( Source.REFSEQ.toString() ) ) ) ) );
490     }
491
492     private static List<UniProtTaxonomy> parseUniProtTaxonomy( final List<String> result ) throws IOException {
493         final List<UniProtTaxonomy> taxonomies = new ArrayList<UniProtTaxonomy>();
494         for( final String line : result ) {
495             if ( ForesterUtil.isEmpty( line ) ) {
496                 // Ignore empty lines.
497             }
498             else if ( line.startsWith( "Taxon" ) ) {
499                 final String[] items = line.split( "\t" );
500                 if ( !( items[ 1 ].equalsIgnoreCase( "Mnemonic" ) && items[ 2 ].equalsIgnoreCase( "Scientific name" )
501                         && items[ 3 ].equalsIgnoreCase( "Common name" ) && items[ 4 ].equalsIgnoreCase( "Synonym" )
502                         && items[ 5 ].equalsIgnoreCase( "Other Names" ) && items[ 6 ].equalsIgnoreCase( "Reviewed" )
503                         && items[ 7 ].equalsIgnoreCase( "Rank" ) && items[ 8 ].equalsIgnoreCase( "Lineage" ) ) ) {
504                     throw new IOException( "Unreconized UniProt Taxonomy format: " + line );
505                 }
506             }
507             else {
508                 if ( line.split( "\t" ).length > 4 ) {
509                     taxonomies.add( new UniProtTaxonomy( line ) );
510                 }
511             }
512         }
513         return taxonomies;
514     }
515 }