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.IOException;
30 import java.io.InputStreamReader;
31 import java.io.UnsupportedEncodingException;
32 import java.net.URL;
33 import java.net.URLConnection;
34 import java.net.URLEncoder;
35 import java.util.ArrayList;
36 import java.util.List;
37 import java.util.SortedSet;
38 import java.util.TreeSet;
39
40 import org.forester.io.parsers.phyloxml.PhyloXmlDataFormatException;
41 import org.forester.phylogeny.Phylogeny;
42 import org.forester.phylogeny.PhylogenyNode;
43 import org.forester.phylogeny.data.Accession;
44 import org.forester.phylogeny.data.Annotation;
45 import org.forester.phylogeny.data.Identifier;
46 import org.forester.phylogeny.data.Sequence;
47 import org.forester.phylogeny.data.Taxonomy;
48 import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
49 import org.forester.util.ForesterUtil;
50 import org.forester.util.SequenceIdParser;
51
52 public final class SequenceDbWsTools {
53
54     public final static String   BASE_UNIPROT_URL  = "http://www.uniprot.org/";
55     public final static String   BASE_EMBL_DB_URL  = "http://www.ebi.ac.uk/Tools/dbfetch/dbfetch/";
56     public final static String   EMBL_DBS_EMBL     = "embl";
57     public final static String   EMBL_DBS_REFSEQ_P = "refseqp";
58     public final static String   EMBL_DBS_REFSEQ_N = "refseqn";
59     private final static String  URL_ENC           = "UTF-8";
60     private final static boolean DEBUG             = false;
61
62     private static List<UniProtTaxonomy> getTaxonomiesFromCommonName( final String cn, final int max_taxonomies_return )
63             throws IOException {
64         final List<String> result = getTaxonomyStringFromCommonName( cn, max_taxonomies_return );
65         if ( result.size() > 0 ) {
66             return parseUniProtTaxonomy( result );
67         }
68         return null;
69     }
70
71     public static List<UniProtTaxonomy> getTaxonomiesFromCommonNameStrict( final String cn,
72                                                                            final int max_taxonomies_return )
73             throws IOException {
74         final List<UniProtTaxonomy> taxonomies = getTaxonomiesFromCommonName( cn, max_taxonomies_return );
75         if ( ( taxonomies != null ) && ( taxonomies.size() > 0 ) ) {
76             final List<UniProtTaxonomy> filtered_taxonomies = new ArrayList<UniProtTaxonomy>();
77             for( final UniProtTaxonomy taxonomy : taxonomies ) {
78                 if ( taxonomy.getCommonName().equalsIgnoreCase( cn ) ) {
79                     filtered_taxonomies.add( taxonomy );
80                 }
81             }
82             return filtered_taxonomies;
83         }
84         return null;
85     }
86
87     public static List<UniProtTaxonomy> getTaxonomiesFromId( final String id, final int max_taxonomies_return )
88             throws IOException {
89         final List<String> result = getTaxonomyStringFromId( id, max_taxonomies_return );
90         if ( result.size() > 0 ) {
91             return parseUniProtTaxonomy( result );
92         }
93         return null;
94     }
95
96     private static List<UniProtTaxonomy> getTaxonomiesFromScientificName( final String sn,
97                                                                           final int max_taxonomies_return )
98             throws IOException {
99         final List<String> result = getTaxonomyStringFromScientificName( sn, max_taxonomies_return );
100         if ( result.size() > 0 ) {
101             return parseUniProtTaxonomy( result );
102         }
103         return null;
104     }
105
106     /**
107      * Does not return "sub-types".
108      * For example, for "Mus musculus" only returns "Mus musculus"
109      * and not "Mus musculus", "Mus musculus bactrianus", ...
110      * 
111      */
112     public static List<UniProtTaxonomy> getTaxonomiesFromScientificNameStrict( final String sn,
113                                                                                final int max_taxonomies_return )
114             throws IOException {
115         final List<UniProtTaxonomy> taxonomies = getTaxonomiesFromScientificName( sn, max_taxonomies_return );
116         if ( ( taxonomies != null ) && ( taxonomies.size() > 0 ) ) {
117             final List<UniProtTaxonomy> filtered_taxonomies = new ArrayList<UniProtTaxonomy>();
118             for( final UniProtTaxonomy taxonomy : taxonomies ) {
119                 if ( taxonomy.getScientificName().equalsIgnoreCase( sn ) ) {
120                     filtered_taxonomies.add( taxonomy );
121                 }
122             }
123             return filtered_taxonomies;
124         }
125         return null;
126     }
127
128     public static List<UniProtTaxonomy> getTaxonomiesFromTaxonomyCode( final String code,
129                                                                        final int max_taxonomies_return )
130             throws IOException {
131         final String my_code = new String( code );
132         final List<String> result = getTaxonomyStringFromTaxonomyCode( my_code, max_taxonomies_return );
133         if ( result.size() > 0 ) {
134             return parseUniProtTaxonomy( result );
135         }
136         return null;
137     }
138
139     public static SequenceDatabaseEntry obtainEmblEntry( final Identifier id, final int max_lines_to_return )
140             throws IOException {
141         final List<String> lines = queryEmblDb( id, max_lines_to_return );
142         return EbiDbEntry.createInstanceFromPlainText( lines );
143     }
144
145     public static SequenceDatabaseEntry obtainRefSeqEntryFromEmbl( final Identifier id, final int max_lines_to_return )
146             throws IOException {
147         final List<String> lines = queryEmblDb( id, max_lines_to_return );
148         return EbiDbEntry.createInstanceFromPlainTextForRefSeq( lines );
149     }
150
151     public static SortedSet<String> obtainSeqInformation( final Phylogeny phy,
152                                                           final boolean ext_nodes_only,
153                                                           final boolean allow_to_set_taxonomic_data,
154                                                           final int lines_to_return ) throws IOException {
155         final SortedSet<String> not_found = new TreeSet<String>();
156         for( final PhylogenyNodeIterator iter = phy.iteratorPostorder(); iter.hasNext(); ) {
157             final PhylogenyNode node = iter.next();
158             if ( ext_nodes_only && node.isInternal() ) {
159                 continue;
160             }
161             String query = null;
162             Identifier id = null;
163             Db db = Db.NONE;
164             if ( node.getNodeData().isHasSequence() && ( node.getNodeData().getSequence().getAccession() != null )
165                     && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() )
166                     && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getValue() )
167                     && node.getNodeData().getSequence().getAccession().getValue().toLowerCase().startsWith( "uniprot" ) ) {
168                 query = node.getNodeData().getSequence().getAccession().getValue();
169                 db = Db.UNIPROT;
170             }
171             else if ( node.getNodeData().isHasSequence()
172                     && ( node.getNodeData().getSequence().getAccession() != null )
173                     && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() )
174                     && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getValue() )
175                     && ( node.getNodeData().getSequence().getAccession().getValue().toLowerCase().startsWith( "embl" ) || node
176                             .getNodeData().getSequence().getAccession().getValue().toLowerCase().startsWith( "ebi" ) ) ) {
177                 query = node.getNodeData().getSequence().getAccession().getValue();
178                 db = Db.EMBL;
179             }
180             else if ( !ForesterUtil.isEmpty( node.getName() ) ) {
181                 if ( ( query = ForesterUtil.extractUniProtKbProteinSeqIdentifier( node ) ) != null ) {
182                     db = Db.UNIPROT;
183                 }
184                 else if ( ( id = SequenceIdParser.parse( node.getName() ) ) != null ) {
185                     if ( id.getProvider().equalsIgnoreCase( Identifier.NCBI ) ) {
186                         db = Db.NCBI;
187                     }
188                     else if ( id.getProvider().equalsIgnoreCase( Identifier.REFSEQ ) ) {
189                         db = Db.REFSEQ;
190                     }
191                 }
192             }
193             if ( db == Db.NONE ) {
194                 not_found.add( node.getName() );
195             }
196             SequenceDatabaseEntry db_entry = null;
197             if ( !ForesterUtil.isEmpty( query ) ) {
198                 if ( db == Db.UNIPROT ) {
199                     if ( DEBUG ) {
200                         System.out.println( "uniprot: " + query );
201                     }
202                     db_entry = obtainUniProtEntry( query, lines_to_return );
203                 }
204                 if ( ( db == Db.EMBL ) || ( ( db == Db.UNIPROT ) && ( db_entry == null ) ) ) {
205                     if ( DEBUG ) {
206                         System.out.println( "embl: " + query );
207                     }
208                     db_entry = obtainEmblEntry( new Identifier( query ), lines_to_return );
209                     if ( ( db == Db.UNIPROT ) && ( db_entry != null ) ) {
210                         db = Db.EMBL;
211                     }
212                 }
213             }
214             else if ( ( db == Db.REFSEQ ) && ( id != null ) ) {
215                 db_entry = obtainRefSeqEntryFromEmbl( id, lines_to_return );
216             }
217             else if ( ( db == Db.NCBI ) && ( id != null ) ) {
218                 db_entry = obtainEmblEntry( id, lines_to_return ); //TODO ?
219             }
220             if ( ( db_entry != null ) && !db_entry.isEmpty() ) {
221                 final Sequence seq = node.getNodeData().isHasSequence() ? node.getNodeData().getSequence()
222                         : new Sequence();
223                 if ( !ForesterUtil.isEmpty( db_entry.getAccession() ) ) {
224                     String type = null;
225                     if ( db == Db.EMBL ) {
226                         type = "embl";
227                     }
228                     else if ( db == Db.UNIPROT ) {
229                         type = "uniprot";
230                     }
231                     else if ( db == Db.NCBI ) {
232                         type = "ncbi";
233                     }
234                     else if ( db == Db.REFSEQ ) {
235                         type = "refseq";
236                     }
237                     seq.setAccession( new Accession( db_entry.getAccession(), type ) );
238                 }
239                 if ( !ForesterUtil.isEmpty( db_entry.getSequenceName() ) ) {
240                     seq.setName( db_entry.getSequenceName() );
241                 }
242                 if ( !ForesterUtil.isEmpty( db_entry.getGeneName() ) ) {
243                     try {
244                         seq.setSymbol( db_entry.getGeneName() );
245                     }
246                     catch ( PhyloXmlDataFormatException e ) {
247                         // Eat this exception.
248                     }
249                 }
250                 if ( !ForesterUtil.isEmpty( db_entry.getGeneName() ) ) {
251                     seq.addAnnotation( new Annotation( "GN", db_entry.getGeneName() ) );
252                 }
253                 final Taxonomy tax = node.getNodeData().isHasTaxonomy() ? node.getNodeData().getTaxonomy()
254                         : new Taxonomy();
255                 if ( !ForesterUtil.isEmpty( db_entry.getTaxonomyScientificName() ) ) {
256                     tax.setScientificName( db_entry.getTaxonomyScientificName() );
257                 }
258                 if ( allow_to_set_taxonomic_data && !ForesterUtil.isEmpty( db_entry.getTaxonomyIdentifier() ) ) {
259                     tax.setIdentifier( new Identifier( db_entry.getTaxonomyIdentifier(), "uniprot" ) );
260                 }
261                 node.getNodeData().setTaxonomy( tax );
262                 node.getNodeData().setSequence( seq );
263             }
264             else if ( db != Db.NONE ) {
265                 not_found.add( node.getName() );
266             }
267             try {
268                 Thread.sleep( 10 );// Sleep for 10 ms
269             }
270             catch ( final InterruptedException ie ) {
271             }
272         }
273         return not_found;
274     }
275
276     public static SequenceDatabaseEntry obtainUniProtEntry( final String query, final int max_lines_to_return )
277             throws IOException {
278         final List<String> lines = queryUniprot( "uniprot/" + query + ".txt", max_lines_to_return );
279         return UniProtEntry.createInstanceFromPlainText( lines );
280     }
281
282     public static List<String> queryDb( final String query, int max_lines_to_return, final String base_url )
283             throws IOException {
284         if ( ForesterUtil.isEmpty( query ) ) {
285             throw new IllegalArgumentException( "illegal attempt to use empty query " );
286         }
287         if ( max_lines_to_return < 1 ) {
288             max_lines_to_return = 1;
289         }
290         final URL url = new URL( base_url + query );
291         if ( DEBUG ) {
292             System.out.println( "url: " + url.toString() );
293         }
294         final URLConnection urlc = url.openConnection();
295         final BufferedReader in = new BufferedReader( new InputStreamReader( urlc.getInputStream() ) );
296         String line;
297         final List<String> result = new ArrayList<String>();
298         while ( ( line = in.readLine() ) != null ) {
299             if ( DEBUG ) {
300                 System.out.println( line );
301             }
302             result.add( line );
303             if ( result.size() > max_lines_to_return ) {
304                 break;
305             }
306         }
307         in.close();
308         try {
309             // To prevent accessing online dbs in too quick succession. 
310             Thread.sleep( 20 );
311         }
312         catch ( final InterruptedException e ) {
313             e.printStackTrace();
314         }
315         return result;
316     }
317
318     public static List<String> queryEmblDb( final Identifier id, final int max_lines_to_return ) throws IOException {
319         final StringBuilder url_sb = new StringBuilder();
320         url_sb.append( BASE_EMBL_DB_URL );
321         if ( ForesterUtil.isEmpty( id.getProvider() ) || id.getProvider().equalsIgnoreCase( Identifier.NCBI ) ) {
322             url_sb.append( SequenceDbWsTools.EMBL_DBS_EMBL );
323             url_sb.append( '/' );
324         }
325         else if ( id.getProvider().equalsIgnoreCase( Identifier.REFSEQ ) ) {
326             if ( id.getValue().toUpperCase().indexOf( 'P' ) == 1 ) {
327                 url_sb.append( SequenceDbWsTools.EMBL_DBS_REFSEQ_P );
328                 url_sb.append( '/' );
329             }
330             else {
331                 url_sb.append( SequenceDbWsTools.EMBL_DBS_REFSEQ_N );
332                 url_sb.append( '/' );
333             }
334         }
335         return queryDb( id.getValue(), max_lines_to_return, url_sb.toString() );
336     }
337
338     public static List<String> queryUniprot( final String query, final int max_lines_to_return ) throws IOException {
339         return queryDb( query, max_lines_to_return, BASE_UNIPROT_URL );
340     }
341
342     private static String encode( final String str ) throws UnsupportedEncodingException {
343         return URLEncoder.encode( str.trim(), URL_ENC );
344     }
345
346     private static List<String> getTaxonomyStringFromCommonName( final String cn, final int max_lines_to_return )
347             throws IOException {
348         return queryUniprot( "taxonomy/?query=common%3a%22" + encode( cn ) + "%22&format=tab", max_lines_to_return );
349     }
350
351     private static List<String> getTaxonomyStringFromId( final String id, final int max_lines_to_return )
352             throws IOException {
353         return queryUniprot( "taxonomy/?query=id%3a%22" + encode( id ) + "%22&format=tab", max_lines_to_return );
354     }
355
356     private static List<String> getTaxonomyStringFromScientificName( final String sn, final int max_lines_to_return )
357             throws IOException {
358         return queryUniprot( "taxonomy/?query=scientific%3a%22" + encode( sn ) + "%22&format=tab", max_lines_to_return );
359     }
360
361     private static List<String> getTaxonomyStringFromTaxonomyCode( final String code, final int max_lines_to_return )
362             throws IOException {
363         return queryUniprot( "taxonomy/?query=mnemonic%3a%22" + encode( code ) + "%22&format=tab", max_lines_to_return );
364     }
365
366     private static List<UniProtTaxonomy> parseUniProtTaxonomy( final List<String> result ) throws IOException {
367         final List<UniProtTaxonomy> taxonomies = new ArrayList<UniProtTaxonomy>();
368         for( final String line : result ) {
369             if ( ForesterUtil.isEmpty( line ) ) {
370                 // Ignore empty lines.
371             }
372             else if ( line.startsWith( "Taxon" ) ) {
373                 final String[] items = line.split( "\t" );
374                 if ( !( items[ 1 ].equalsIgnoreCase( "Mnemonic" ) && items[ 2 ].equalsIgnoreCase( "Scientific name" )
375                         && items[ 3 ].equalsIgnoreCase( "Common name" ) && items[ 4 ].equalsIgnoreCase( "Synonym" )
376                         && items[ 5 ].equalsIgnoreCase( "Other Names" ) && items[ 6 ].equalsIgnoreCase( "Reviewed" )
377                         && items[ 7 ].equalsIgnoreCase( "Rank" ) && items[ 8 ].equalsIgnoreCase( "Lineage" ) ) ) {
378                     throw new IOException( "Unreconized UniProt Taxonomy format: " + line );
379                 }
380             }
381             else {
382                 if ( line.split( "\t" ).length > 4 ) {
383                     taxonomies.add( new UniProtTaxonomy( line ) );
384                 }
385             }
386         }
387         return taxonomies;
388     }
389
390     public enum Db {
391         UNIPROT, EMBL, NCBI, NONE, REFSEQ;
392     }
393 }