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