fixed some minor issues
[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                      = false;
66     private final static String  URL_ENC                    = "UTF-8";
67     private final static int     SLEEP                      = 200;
68     private static final boolean ALLOW_TO_OVERWRITE_MOL_SEQ = false;
69
70     public static List<UniProtTaxonomy> getTaxonomiesFromCommonNameStrict( final String cn,
71                                                                            final int max_taxonomies_return )
72             throws IOException {
73         final List<UniProtTaxonomy> taxonomies = getTaxonomiesFromCommonName( cn, max_taxonomies_return );
74         if ( ( taxonomies != null ) && ( taxonomies.size() > 0 ) ) {
75             final List<UniProtTaxonomy> filtered_taxonomies = new ArrayList<UniProtTaxonomy>();
76             for( final UniProtTaxonomy taxonomy : taxonomies ) {
77                 if ( taxonomy.getCommonName().equalsIgnoreCase( cn ) ) {
78                     filtered_taxonomies.add( taxonomy );
79                 }
80             }
81             return filtered_taxonomies;
82         }
83         return null;
84     }
85
86     public static List<UniProtTaxonomy> getTaxonomiesFromId( final String id, final int max_taxonomies_return )
87             throws IOException {
88         final List<String> result = getTaxonomyStringFromId( id, max_taxonomies_return );
89         if ( result.size() > 0 ) {
90             return parseUniProtTaxonomy( result );
91         }
92         return null;
93     }
94
95     /**
96      * Does not return "sub-types".
97      * For example, for "Mus musculus" only returns "Mus musculus"
98      * and not "Mus musculus", "Mus musculus bactrianus", ...
99      *
100      */
101     public static List<UniProtTaxonomy> getTaxonomiesFromScientificNameStrict( final String sn,
102                                                                                final int max_taxonomies_return )
103             throws IOException {
104         final List<UniProtTaxonomy> taxonomies = getTaxonomiesFromScientificName( sn, max_taxonomies_return );
105         if ( ( taxonomies != null ) && ( taxonomies.size() > 0 ) ) {
106             final List<UniProtTaxonomy> filtered_taxonomies = new ArrayList<UniProtTaxonomy>();
107             for( final UniProtTaxonomy taxonomy : taxonomies ) {
108                 if ( taxonomy.getScientificName().equalsIgnoreCase( sn ) ) {
109                     filtered_taxonomies.add( taxonomy );
110                 }
111             }
112             return filtered_taxonomies;
113         }
114         return null;
115     }
116
117     public static List<UniProtTaxonomy> getTaxonomiesFromTaxonomyCode( final String code,
118                                                                        final int max_taxonomies_return )
119             throws IOException {
120         final String my_code = new String( code );
121         final List<String> result = getTaxonomyStringFromTaxonomyCode( my_code, max_taxonomies_return );
122         if ( result.size() > 0 ) {
123             return parseUniProtTaxonomy( result );
124         }
125         return null;
126     }
127
128     public static SequenceDatabaseEntry obtainEmblEntry( final Accession acc ) throws IOException {
129         return obtainEmblEntry( acc, DEFAULT_LINES_TO_RETURN );
130     }
131
132     public static SequenceDatabaseEntry obtainEmblEntry( final Accession acc, final int max_lines_to_return )
133             throws IOException {
134         final List<String> lines = queryEmblDb( acc, max_lines_to_return );
135         return EbiDbEntry.createInstance( lines );
136     }
137
138     public static SequenceDatabaseEntry obtainEntry( final String acc_str ) throws IOException {
139         if ( ForesterUtil.isEmpty( acc_str ) ) {
140             throw new IllegalArgumentException( "cannot not extract sequence db accessor from null or empty string" );
141         }
142         final Accession acc = SequenceAccessionTools.parseAccessorFromString( acc_str );
143         if ( acc == null ) {
144             throw new IllegalArgumentException( "could not extract acceptable sequence db accessor from \"" + acc_str
145                     + "\"" );
146         }
147         if ( acc.getSource().equals( Source.REFSEQ.toString() ) || acc.getSource().equals( Source.EMBL.toString() )
148                 || acc.getSource().equals( Source.NCBI.toString() ) ) {
149             return obtainEmblEntry( acc, DEFAULT_LINES_TO_RETURN );
150         }
151         else if ( acc.getSource().equals( Source.UNIPROT.toString() ) ) {
152             return obtainUniProtEntry( acc.getValue(), DEFAULT_LINES_TO_RETURN );
153         }
154         else {
155             throw new IllegalArgumentException( "don't know how to handle request for source \"" + acc.getSource()
156                     + "\"" );
157         }
158     }
159
160     public static SequenceDatabaseEntry obtainRefSeqEntryFromEmbl( final Accession acc ) throws IOException {
161         return obtainRefSeqEntryFromEmbl( acc, DEFAULT_LINES_TO_RETURN );
162     }
163
164     public static SequenceDatabaseEntry obtainRefSeqEntryFromEmbl( final Accession acc, final int max_lines_to_return )
165             throws IOException {
166         final List<String> lines = queryEmblDbForRefSeqEntry( acc, max_lines_to_return );
167         return EbiDbEntry.createInstance( lines );
168     }
169
170     public final static Accession obtainSeqAccession( final PhylogenyNode node ) {
171         Accession acc = SequenceAccessionTools.obtainFromSeqAccession( node );
172         if ( !isAccessionAcceptable( acc ) ) {
173             acc = SequenceAccessionTools.obtainAccessorFromDataFields( node );
174         }
175         return acc;
176     }
177
178     public final static void obtainSeqInformation( final boolean allow_to_set_taxonomic_data,
179                                                    final int lines_to_return,
180                                                    final SortedSet<String> not_found,
181                                                    final PhylogenyNode node ) throws IOException {
182         final Accession acc = obtainSeqAccession( node );
183         if ( !isAccessionAcceptable( acc ) ) {
184             if ( node.isExternal() || !node.isEmpty() ) {
185                 not_found.add( node.toString() );
186             }
187         }
188         else {
189             addDataFromDbToNode( allow_to_set_taxonomic_data, lines_to_return, not_found, node, acc );
190         }
191     }
192
193     public final static void obtainSeqInformation( final boolean allow_to_set_taxonomic_data,
194                                                    final SortedSet<String> not_found,
195                                                    final PhylogenyNode node ) throws IOException {
196         obtainSeqInformation( allow_to_set_taxonomic_data, DEFAULT_LINES_TO_RETURN, not_found, node );
197     }
198
199     public final static SortedSet<String> obtainSeqInformation( final Phylogeny phy,
200                                                                 final boolean ext_nodes_only,
201                                                                 final boolean allow_to_set_taxonomic_data,
202                                                                 final int lines_to_return ) throws IOException {
203         final SortedSet<String> not_found = new TreeSet<String>();
204         for( final PhylogenyNodeIterator iter = phy.iteratorPostorder(); iter.hasNext(); ) {
205             final PhylogenyNode node = iter.next();
206             if ( node.isExternal() || !ext_nodes_only ) {
207                 obtainSeqInformation( allow_to_set_taxonomic_data, lines_to_return, not_found, node );
208             }
209         }
210         return not_found;
211     }
212
213     public final static void obtainSeqInformation( final PhylogenyNode node ) throws IOException {
214         obtainSeqInformation( true, DEFAULT_LINES_TO_RETURN, new TreeSet<String>(), node );
215     }
216
217     public static SequenceDatabaseEntry obtainUniProtEntry( final String query ) throws IOException {
218         return obtainUniProtEntry( query, DEFAULT_LINES_TO_RETURN );
219     }
220
221     public static SequenceDatabaseEntry obtainUniProtEntry( final String query, final int max_lines_to_return )
222             throws IOException {
223         final List<String> lines = queryUniprot( "uniprot/" + query + ".txt", max_lines_to_return );
224         return UniProtEntry.createInstance( lines );
225     }
226
227     public static List<String> queryDb( final String query, int max_lines_to_return, final String base_url )
228             throws IOException {
229         if ( ForesterUtil.isEmpty( query ) ) {
230             throw new IllegalArgumentException( "illegal attempt to use empty query " );
231         }
232         if ( max_lines_to_return < 1 ) {
233             max_lines_to_return = 1;
234         }
235         final URL url = new URL( base_url + query );
236         if ( DEBUG ) {
237             System.out.println( "url: " + url.toString() );
238         }
239         final URLConnection urlc = url.openConnection();
240         final BufferedReader in = new BufferedReader( new InputStreamReader( urlc.getInputStream() ) );
241         String line;
242         final List<String> result = new ArrayList<String>();
243         while ( ( line = in.readLine() ) != null ) {
244             if ( DEBUG ) {
245                 System.out.println( line );
246             }
247             result.add( line );
248             if ( result.size() > max_lines_to_return ) {
249                 break;
250             }
251         }
252         in.close();
253         try {
254             // To prevent accessing online dbs in too quick succession.
255             Thread.sleep( SLEEP );
256         }
257         catch ( final InterruptedException e ) {
258             e.printStackTrace();
259         }
260         return result;
261     }
262
263     public static List<String> queryEmblDb( final Accession acc, final int max_lines_to_return ) throws IOException {
264         final StringBuilder url_sb = new StringBuilder();
265         //  url_sb.append( BASE_EMBL_DB_URL );
266         if ( DEBUG ) {
267             System.out.println( "source: " + acc.getSource() );
268         }
269         if ( acc.getSource().equals( Source.NCBI.toString() ) ) {
270             url_sb.append( EMBL_GENBANK );
271             //url_sb.append( '/' );
272         }
273         else if ( acc.getSource().equals( Source.REFSEQ.toString() ) ) {
274             url_sb.append( EMBL_REFSEQ );
275         }
276         else if ( acc.getSource().equals( Source.EMBL.toString() ) ) {
277             url_sb.append( EMBL_EMBL );
278         }
279         else {
280             throw new IllegalArgumentException( "unable to handle source: " + acc.getSource() );
281         }
282         return queryDb( acc.getValue(), max_lines_to_return, url_sb.toString() );
283     }
284
285     public static List<String> queryEmblDbForRefSeqEntry( final Accession id, final int max_lines_to_return )
286             throws IOException {
287         final StringBuilder url_sb = new StringBuilder();
288         url_sb.append( EMBL_REFSEQ );
289         return queryDb( id.getValue(), max_lines_to_return, url_sb.toString() );
290     }
291
292     public static List<String> queryUniprot( final String query, final int max_lines_to_return ) throws IOException {
293         return queryDb( query, max_lines_to_return, BASE_UNIPROT_URL );
294     }
295
296     final static String extractFrom( final String target, final String a ) {
297         final int i_a = target.indexOf( a );
298         return target.substring( i_a + a.length() ).trim();
299     }
300
301     final static String extractFromTo( final String target, final String a, final String b ) {
302         final int i_a = target.indexOf( a );
303         final int i_b = target.indexOf( b );
304         if ( ( i_a < 0 ) || ( i_b < i_a ) ) {
305             return "";
306         }
307         return target.substring( i_a + a.length(), i_b ).trim();
308     }
309
310     final static String extractTo( final String target, final String b ) {
311         final int i_b = target.indexOf( b );
312         return target.substring( 0, i_b ).trim();
313     }
314
315     private static void addDataFromDbToNode( final boolean allow_to_set_taxonomic_data,
316                                              final int lines_to_return,
317                                              final SortedSet<String> not_found,
318                                              final PhylogenyNode node,
319                                              final Accession acc ) throws IOException {
320         SequenceDatabaseEntry db_entry = null;
321         final String query = acc.getValue();
322         if ( acc.getSource().equals( Source.UNIPROT.toString() ) ) {
323             if ( DEBUG ) {
324                 System.out.println( "uniprot: " + query );
325             }
326             try {
327                 db_entry = obtainUniProtEntry( query, lines_to_return );
328             }
329             catch ( final FileNotFoundException e ) {
330                 // Eat this, and move to next.
331             }
332         }
333         else if ( acc.getSource().equals( Source.REFSEQ.toString() ) ) {
334             if ( DEBUG ) {
335                 System.out.println( "refseq: " + query );
336             }
337             try {
338                 db_entry = obtainRefSeqEntryFromEmbl( new Accession( query ), lines_to_return );
339             }
340             catch ( final FileNotFoundException e ) {
341                 // Eat this, and move to next.
342             }
343         }
344         else if ( acc.getSource().equals( Source.EMBL.toString() ) || acc.getSource().equals( Source.NCBI.toString() )
345                 || acc.getSource().equals( Source.EMBL.toString() ) ) {
346             if ( DEBUG ) {
347                 System.out.println( acc.toString() );
348             }
349             try {
350                 db_entry = obtainEmblEntry( acc, lines_to_return );
351             }
352             catch ( final FileNotFoundException e ) {
353                 // Eat this, and move to next.
354             }
355         }
356         else if ( acc.getSource().equals( Source.GI.toString() ) ) {
357             if ( DEBUG ) {
358                 System.out.println( "gi: " + query );
359             }
360             try {
361                 db_entry = obtainRefSeqEntryFromEmbl( new Accession( query ), lines_to_return );
362             }
363             catch ( final FileNotFoundException e ) {
364                 // Eat this, and move to next.
365             }
366         }
367         if ( ( db_entry != null ) && !db_entry.isEmpty() ) {
368             final Sequence seq = node.getNodeData().isHasSequence() ? node.getNodeData().getSequence() : new Sequence();
369             if ( !ForesterUtil.isEmpty( db_entry.getAccession() ) ) {
370                 seq.setAccession( new Accession( db_entry.getAccession(), acc.getSource() ) );
371             }
372             if ( !ForesterUtil.isEmpty( db_entry.getSequenceName() ) ) {
373                 seq.setName( db_entry.getSequenceName() );
374             }
375             if ( !ForesterUtil.isEmpty( db_entry.getGeneName() ) ) {
376                 seq.setGeneName( db_entry.getGeneName() );
377             }
378             if ( !ForesterUtil.isEmpty( db_entry.getSequenceSymbol() ) ) {
379                 try {
380                     seq.setSymbol( db_entry.getSequenceSymbol() );
381                 }
382                 catch ( final PhyloXmlDataFormatException e ) {
383                     // Eat this exception.
384                 }
385             }
386             if ( ( db_entry.getMolecularSequence() != null )
387                     && !ForesterUtil.isEmpty( db_entry.getMolecularSequence().getMolecularSequenceAsString() )
388                     && ( ALLOW_TO_OVERWRITE_MOL_SEQ || seq.getMolecularSequence().isEmpty() ) ) {
389                 seq.setMolecularSequence( db_entry.getMolecularSequence().getMolecularSequenceAsString() );
390                 seq.setMolecularSequenceAligned( false );
391                 if ( db_entry.getMolecularSequence().getType() == TYPE.AA ) {
392                     seq.setType( "protein" );
393                 }
394                 else if ( db_entry.getMolecularSequence().getType() == TYPE.DNA ) {
395                     seq.setType( "dna" );
396                 }
397                 else if ( db_entry.getMolecularSequence().getType() == TYPE.RNA ) {
398                     seq.setType( "rna" );
399                 }
400             }
401             if ( ( db_entry.getGoTerms() != null ) && !db_entry.getGoTerms().isEmpty() ) {
402                 for( final GoTerm go : db_entry.getGoTerms() ) {
403                     final Annotation ann = new Annotation( go.getGoId().getId() );
404                     ann.setDesc( go.getName() );
405                     seq.addAnnotation( ann );
406                 }
407             }
408             if ( ( db_entry.getCrossReferences() != null ) && !db_entry.getCrossReferences().isEmpty() ) {
409                 for( final Accession x : db_entry.getCrossReferences() ) {
410                     seq.addCrossReference( x );
411                 }
412             }
413             if ( !ForesterUtil.isEmpty( db_entry.getChromosome() ) && !ForesterUtil.isEmpty( db_entry.getMap() ) ) {
414                 seq.setLocation( "chr " + db_entry.getChromosome() + ", " + db_entry.getMap() );
415             }
416             else if ( !ForesterUtil.isEmpty( db_entry.getChromosome() ) ) {
417                 seq.setLocation( "chr " + db_entry.getChromosome() );
418             }
419             else if ( !ForesterUtil.isEmpty( db_entry.getMap() ) ) {
420                 seq.setLocation( db_entry.getMap() );
421             }
422             final Taxonomy tax = node.getNodeData().isHasTaxonomy() ? node.getNodeData().getTaxonomy() : new Taxonomy();
423             if ( !ForesterUtil.isEmpty( db_entry.getTaxonomyScientificName() ) ) {
424                 tax.setScientificName( db_entry.getTaxonomyScientificName() );
425             }
426             if ( allow_to_set_taxonomic_data && !ForesterUtil.isEmpty( db_entry.getTaxonomyIdentifier() ) ) {
427                 tax.setIdentifier( new Identifier( db_entry.getTaxonomyIdentifier(), "uniprot" ) );
428             }
429             node.getNodeData().setTaxonomy( tax );
430             node.getNodeData().setSequence( seq );
431         }
432         else {
433             if ( node.isExternal() || !node.isEmpty() ) {
434                 not_found.add( node.toString() );
435             }
436         }
437         try {
438             Thread.sleep( SLEEP );
439         }
440         catch ( final InterruptedException ie ) {
441         }
442     }
443
444     private static String encode( final String str ) throws UnsupportedEncodingException {
445         return URLEncoder.encode( str.trim(), URL_ENC );
446     }
447
448     private static List<UniProtTaxonomy> getTaxonomiesFromCommonName( final String cn, final int max_taxonomies_return )
449             throws IOException {
450         final List<String> result = getTaxonomyStringFromCommonName( cn, max_taxonomies_return );
451         if ( result.size() > 0 ) {
452             return parseUniProtTaxonomy( result );
453         }
454         return null;
455     }
456
457     private static List<UniProtTaxonomy> getTaxonomiesFromScientificName( final String sn,
458                                                                           final int max_taxonomies_return )
459             throws IOException {
460         final List<String> result = getTaxonomyStringFromScientificName( sn, max_taxonomies_return );
461         if ( result.size() > 0 ) {
462             return parseUniProtTaxonomy( result );
463         }
464         return null;
465     }
466
467     private static List<String> getTaxonomyStringFromCommonName( final String cn, final int max_lines_to_return )
468             throws IOException {
469         return queryUniprot( "taxonomy/?query=common%3a%22" + encode( cn ) + "%22&format=tab", max_lines_to_return );
470     }
471
472     private static List<String> getTaxonomyStringFromId( final String id, final int max_lines_to_return )
473             throws IOException {
474         return queryUniprot( "taxonomy/?query=id%3a%22" + encode( id ) + "%22&format=tab", max_lines_to_return );
475     }
476
477     private static List<String> getTaxonomyStringFromScientificName( final String sn, final int max_lines_to_return )
478             throws IOException {
479         return queryUniprot( "taxonomy/?query=scientific%3a%22" + encode( sn ) + "%22&format=tab", max_lines_to_return );
480     }
481
482     private static List<String> getTaxonomyStringFromTaxonomyCode( final String code, final int max_lines_to_return )
483             throws IOException {
484         return queryUniprot( "taxonomy/?query=mnemonic%3a%22" + encode( code ) + "%22&format=tab", max_lines_to_return );
485     }
486
487     private final static boolean isAccessionAcceptable( final Accession acc ) {
488         return ( !( ( acc == null ) || ForesterUtil.isEmpty( acc.getSource() ) || ForesterUtil.isEmpty( acc.getValue() ) || ( ( acc
489                 .getSource().equals( Source.UNIPROT.toString() ) )
490                 && ( acc.getSource().toString().equals( Source.EMBL.toString() ) ) && ( acc.getSource().toString()
491                 .equals( Source.REFSEQ.toString() ) ) ) ) );
492     }
493
494     private static List<UniProtTaxonomy> parseUniProtTaxonomy( final List<String> result ) throws IOException {
495         final List<UniProtTaxonomy> taxonomies = new ArrayList<UniProtTaxonomy>();
496         for( final String line : result ) {
497             if ( ForesterUtil.isEmpty( line ) ) {
498                 // Ignore empty lines.
499             }
500             else if ( line.startsWith( "Taxon" ) ) {
501                 final String[] items = line.split( "\t" );
502                 if ( !( items[ 1 ].equalsIgnoreCase( "Mnemonic" ) && items[ 2 ].equalsIgnoreCase( "Scientific name" )
503                         && items[ 3 ].equalsIgnoreCase( "Common name" ) && items[ 4 ].equalsIgnoreCase( "Synonym" )
504                         && items[ 5 ].equalsIgnoreCase( "Other Names" ) && items[ 6 ].equalsIgnoreCase( "Reviewed" )
505                         && items[ 7 ].equalsIgnoreCase( "Rank" ) && items[ 8 ].equalsIgnoreCase( "Lineage" ) ) ) {
506                     throw new IOException( "Unreconized UniProt Taxonomy format: " + line );
507                 }
508             }
509             else {
510                 if ( line.split( "\t" ).length > 4 ) {
511                     taxonomies.add( new UniProtTaxonomy( line ) );
512                 }
513             }
514         }
515         return taxonomies;
516     }
517 }