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