in progress...
[jalview.git] / forester / java / src / org / forester / analysis / TaxonomyDataManager.java
1 // $Id:
2 //
3 // forester -- software libraries and applications
4 // for genomics and evolutionary biology research.
5 //
6 // Copyright (C) 2010 Christian M Zmasek
7 // Copyright (C) 2010 Sanford-Burnham Medical Research Institute
8 // All rights reserved
9 //
10 // This library is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU Lesser General Public
12 // License as published by the Free Software Foundation; either
13 // version 2.1 of the License, or (at your option) any later version.
14 //
15 // This library is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 // Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public
21 // License along with this library; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 //
24 // Contact: phylosoft @ gmail . com
25 // WWW: https://sites.google.com/site/cmzmasek/home/software/forester
26
27 package org.forester.analysis;
28
29 import java.io.IOException;
30 import java.net.UnknownHostException;
31 import java.util.ArrayList;
32 import java.util.HashMap;
33 import java.util.List;
34 import java.util.SortedSet;
35 import java.util.TreeSet;
36 import java.util.regex.Matcher;
37
38 import javax.swing.JOptionPane;
39
40 import org.forester.archaeopteryx.MainFrameApplication;
41 import org.forester.archaeopteryx.TreePanel;
42 import org.forester.archaeopteryx.tools.AncestralTaxonomyInferrer;
43 import org.forester.archaeopteryx.tools.RunnableProcess;
44 import org.forester.io.parsers.nhx.NHXParser;
45 import org.forester.io.parsers.phyloxml.PhyloXmlDataFormatException;
46 import org.forester.io.parsers.util.ParserUtils;
47 import org.forester.phylogeny.Phylogeny;
48 import org.forester.phylogeny.PhylogenyNode;
49 import org.forester.phylogeny.data.Identifier;
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.TaxonomyUtil;
54 import org.forester.ws.seqdb.SequenceDbWsTools;
55 import org.forester.ws.seqdb.UniProtTaxonomy;
56
57 public final class TaxonomyDataManager extends RunnableProcess {
58
59     enum QUERY_TYPE {
60         CODE, SN, CN, ID, LIN;
61     }
62     private static final int                              MAX_CACHE_SIZE           = 100000;
63     private static final int                              MAX_TAXONOMIES_TO_RETURN = 2000;
64     private static final HashMap<String, UniProtTaxonomy> _sn_up_cache_map         = new HashMap<String, UniProtTaxonomy>();
65     private static final HashMap<String, UniProtTaxonomy> _lineage_up_cache_map    = new HashMap<String, UniProtTaxonomy>();
66     private static final HashMap<String, UniProtTaxonomy> _code_up_cache_map       = new HashMap<String, UniProtTaxonomy>();
67     private static final HashMap<String, UniProtTaxonomy> _cn_up_cache_map         = new HashMap<String, UniProtTaxonomy>();
68     private static final HashMap<String, UniProtTaxonomy> _id_up_cache_map         = new HashMap<String, UniProtTaxonomy>();
69     private final Phylogeny                               _phy;
70     private final MainFrameApplication                    _mf;
71     private final TreePanel                               _treepanel;
72     private final boolean                                 _delete;
73     private final boolean                                 _allow_simple_names;
74
75     public TaxonomyDataManager( final MainFrameApplication mf, final TreePanel treepanel, final Phylogeny phy ) {
76         _phy = phy;
77         _mf = mf;
78         _treepanel = treepanel;
79         _delete = false;
80         _allow_simple_names = false;
81     }
82
83     public TaxonomyDataManager( final MainFrameApplication mf,
84                                 final TreePanel treepanel,
85                                 final Phylogeny phy,
86                                 final boolean delete,
87                                 final boolean allow_simple_name ) {
88         _phy = phy;
89         _mf = mf;
90         _treepanel = treepanel;
91         _delete = delete;
92         _allow_simple_names = allow_simple_name;
93     }
94
95     synchronized static void clearCachesIfTooLarge() {
96         if ( getSnTaxCacheMap().size() > MAX_CACHE_SIZE ) {
97             getSnTaxCacheMap().clear();
98         }
99         if ( getLineageTaxCacheMap().size() > MAX_CACHE_SIZE ) {
100             getLineageTaxCacheMap().clear();
101         }
102         if ( getCnTaxCacheMap().size() > MAX_CACHE_SIZE ) {
103             getCnTaxCacheMap().clear();
104         }
105         if ( getCodeTaxCacheMap().size() > MAX_CACHE_SIZE ) {
106             getCodeTaxCacheMap().clear();
107         }
108         if ( getIdTaxCacheMap().size() > MAX_CACHE_SIZE ) {
109             getIdTaxCacheMap().clear();
110         }
111     }
112
113     synchronized final static HashMap<String, UniProtTaxonomy> getCnTaxCacheMap() {
114         return _cn_up_cache_map;
115     }
116
117     synchronized final static HashMap<String, UniProtTaxonomy> getCodeTaxCacheMap() {
118         return _code_up_cache_map;
119     }
120
121     synchronized final static HashMap<String, UniProtTaxonomy> getIdTaxCacheMap() {
122         return _id_up_cache_map;
123     }
124
125     synchronized final static HashMap<String, UniProtTaxonomy> getLineageTaxCacheMap() {
126         return _lineage_up_cache_map;
127     }
128
129     synchronized final static HashMap<String, UniProtTaxonomy> getSnTaxCacheMap() {
130         return _sn_up_cache_map;
131     }
132
133     
134     @SuppressWarnings("unchecked")
135     private final static UniProtTaxonomy obtainTaxonomy( final HashMap<String, UniProtTaxonomy> cache,
136                                                          final Object query,
137                                                          final QUERY_TYPE qt ) throws IOException,
138                                                          AncestralTaxonomyInferenceException {
139         if ( cache.containsKey( query ) ) {
140             return cache.get( query ).copy();
141         }
142         else {
143             List<UniProtTaxonomy> up_taxonomies = null;
144             switch ( qt ) {
145                 case ID:
146                     up_taxonomies = getTaxonomiesFromId( ( String ) query );
147                     break;
148                 case CODE:
149                     up_taxonomies = getTaxonomiesFromTaxonomyCode( ( String ) query );
150                     break;
151                 case SN:
152                     up_taxonomies = getTaxonomiesFromScientificName( ( String ) query );
153                     break;
154                 case CN:
155                     up_taxonomies = getTaxonomiesFromCommonName( ( String ) query );
156                     break;
157                 case LIN:
158                     return obtainUniProtTaxonomyFromLineage( ( List<String> ) query );
159                     
160                 default:
161                     throw new RuntimeException();
162             }
163             if ( ( up_taxonomies != null ) && ( up_taxonomies.size() == 1 ) ) {
164                 final UniProtTaxonomy up_tax = up_taxonomies.get( 0 );
165                 if ( !ForesterUtil.isEmpty( up_tax.getScientificName() ) ) {
166                     TaxonomyDataManager.getSnTaxCacheMap().put( up_tax.getScientificName(), up_tax );
167                 }
168                 if ( !ForesterUtil.isEmpty( up_tax.getCode() ) ) {
169                     TaxonomyDataManager.getCodeTaxCacheMap().put( up_tax.getCode(), up_tax );
170                 }
171                 if ( !ForesterUtil.isEmpty( up_tax.getCommonName() ) ) {
172                     TaxonomyDataManager.getCnTaxCacheMap().put( up_tax.getCommonName(), up_tax );
173                 }
174                 if ( !ForesterUtil.isEmpty( up_tax.getId() ) ) {
175                     TaxonomyDataManager.getIdTaxCacheMap().put( up_tax.getId(), up_tax );
176                 }
177                 return up_tax;
178             }
179             else {
180                 return null;
181             }
182         }
183     }
184
185     private final static List<UniProtTaxonomy> getTaxonomiesFromCommonName( final String query ) throws IOException {
186         return SequenceDbWsTools.getTaxonomiesFromCommonNameStrict( query, MAX_TAXONOMIES_TO_RETURN );
187     }
188
189     private final static List<UniProtTaxonomy> getTaxonomiesFromId( final String query ) throws IOException {
190         return SequenceDbWsTools.getTaxonomiesFromId( query, MAX_TAXONOMIES_TO_RETURN );
191     }
192
193     private final static List<UniProtTaxonomy> getTaxonomiesFromScientificName( final String query ) throws IOException {
194         if ( query.equalsIgnoreCase( UniProtTaxonomy.BACTERIA ) || query.equalsIgnoreCase( UniProtTaxonomy.ARCHAEA )
195                 || query.equalsIgnoreCase( UniProtTaxonomy.VIRUSES )
196                 || query.equalsIgnoreCase( UniProtTaxonomy.EUKARYOTA ) || query.equalsIgnoreCase( UniProtTaxonomy.X ) ) {
197             final List<UniProtTaxonomy> l = new ArrayList<UniProtTaxonomy>();
198             l.add( UniProtTaxonomy.createSpecialFromScientificName( query ) );
199             return l;
200         }
201         return SequenceDbWsTools.getTaxonomiesFromScientificNameStrict( query, MAX_TAXONOMIES_TO_RETURN );
202     }
203
204     private final static List<UniProtTaxonomy> getTaxonomiesFromTaxonomyCode( final String query ) throws IOException {
205         //FIXME fix "SPHAR" issue
206         if ( ( ( query.indexOf( "XX" ) == 3 ) && TaxonomyUtil.isHasTaxIdFromFakeTaxCode( query ) )
207                 || query.equals( "SPHAR" ) /* TODO remove me, is same as Sphingomonas aromaticivorans */
208                 ) {
209             final int id = TaxonomyUtil.getTaxIdFromFakeTaxCode( query );
210             return SequenceDbWsTools.getTaxonomiesFromId( String.valueOf( id ), MAX_TAXONOMIES_TO_RETURN );
211         }
212         return SequenceDbWsTools.getTaxonomiesFromTaxonomyCode( query, MAX_TAXONOMIES_TO_RETURN );
213     }
214
215     static final boolean isHasAppropriateId( final Taxonomy tax ) {
216         return ( ( tax.getIdentifier() != null ) && ( !ForesterUtil.isEmpty( tax.getIdentifier().getValue() ) && ( tax
217                 .getIdentifier().getProvider().equalsIgnoreCase( "ncbi" )
218                 || tax.getIdentifier().getProvider().equalsIgnoreCase( "uniprot" ) || tax.getIdentifier().getProvider()
219                 .equalsIgnoreCase( "uniprotkb" ) ) ) );
220     }
221
222     synchronized final private static SortedSet<String> obtainDetailedTaxonomicInformation( final Phylogeny phy,
223                                                                                             final boolean delete,
224                                                                                             final boolean allow_to_use_basic_node_names )
225                                                                                                     throws IOException, AncestralTaxonomyInferenceException {
226         clearCachesIfTooLarge();
227         final SortedSet<String> not_found = new TreeSet<String>();
228         List<PhylogenyNode> not_found_external_nodes = null;
229         if ( delete ) {
230             not_found_external_nodes = new ArrayList<PhylogenyNode>();
231         }
232         for( final PhylogenyNodeIterator iter = phy.iteratorPostorder(); iter.hasNext(); ) {
233             final PhylogenyNode node = iter.next();
234             final QUERY_TYPE qt = null;
235             Taxonomy tax = null;
236             if ( node.getNodeData().isHasTaxonomy() ) {
237                 tax = node.getNodeData().getTaxonomy();
238             }
239             else if ( allow_to_use_basic_node_names && !ForesterUtil.isEmpty( node.getName() ) ) {
240                 // Nothing to be done.
241             }
242             else if ( node.isExternal() ) {
243                 if ( !ForesterUtil.isEmpty( node.getName() ) ) {
244                     not_found.add( node.getName() );
245                 }
246                 else {
247                     not_found.add( node.toString() );
248                 }
249                 if ( delete ) {
250                     not_found_external_nodes.add( node );
251                 }
252             }
253             UniProtTaxonomy uniprot_tax = null;
254             if ( ( ( tax != null ) && ( isHasAppropriateId( tax ) || !ForesterUtil.isEmpty( tax.getScientificName() )
255                     || !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) || !ForesterUtil.isEmpty( tax.getCommonName() ) ) )
256                     || ( allow_to_use_basic_node_names && !ForesterUtil.isEmpty( node.getName() ) ) ) {
257                 if ( ( ( tax != null ) && ( isHasAppropriateId( tax )
258                         || !ForesterUtil.isEmpty( tax.getScientificName() )
259                         || !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) || !ForesterUtil
260                         .isEmpty( tax.getCommonName() ) ) ) ) {
261                     uniprot_tax = obtainUniProtTaxonomy( tax, null, qt );
262                 }
263                 else {
264                     uniprot_tax = obtainUniProtTaxonomy( node.getName(), qt );
265                 }
266                 if ( uniprot_tax != null ) {
267                     if ( tax == null ) {
268                         tax = new Taxonomy();
269                         node.getNodeData().addTaxonomy( tax );
270                     }
271                     updateTaxonomy( qt, node, tax, uniprot_tax );
272                 }
273                 else {
274                     if ( tax != null ) {
275                         not_found.add( tax.toString() );
276                     }
277                     else {
278                         not_found.add( node.getName() );
279                     }
280                     if ( delete && node.isExternal() ) {
281                         not_found_external_nodes.add( node );
282                     }
283                 }
284             }
285         }
286         if ( delete ) {
287             for( final PhylogenyNode node : not_found_external_nodes ) {
288                 phy.deleteSubtree( node, true );
289             }
290             phy.externalNodesHaveChanged();
291             phy.clearHashIdToNodeMap();
292             phy.recalculateNumberOfExternalDescendants( true );
293         }
294         return not_found;
295     }
296
297     public final static UniProtTaxonomy obtainUniProtTaxonomy( final Taxonomy tax, Object query, QUERY_TYPE qt )
298             throws IOException, AncestralTaxonomyInferenceException {
299         if ( tax == null ) {
300             throw new IllegalArgumentException( "illegal attempt to use empty taxonomy object" );
301         }
302         if ( TaxonomyDataManager.isHasAppropriateId( tax ) ) {
303             query = tax.getIdentifier().getValue();
304             qt = QUERY_TYPE.ID;
305             return obtainTaxonomy( TaxonomyDataManager.getIdTaxCacheMap(), query, qt );
306         }
307         else if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
308             if ( !ForesterUtil.isEmpty( tax.getLineage() ) ) {
309                 query = tax.getLineage();
310                 qt = QUERY_TYPE.LIN;
311                 return obtainTaxonomy( TaxonomyDataManager.getLineageTaxCacheMap(), query, qt );
312             }
313             else {
314                 query = tax.getScientificName();
315                 qt = QUERY_TYPE.SN;
316                 return obtainTaxonomy( TaxonomyDataManager.getSnTaxCacheMap(), query, qt );
317             }
318         }
319         else if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
320             query = tax.getTaxonomyCode();
321             qt = QUERY_TYPE.CODE;
322             return obtainTaxonomy( TaxonomyDataManager.getCodeTaxCacheMap(), query, qt );
323         }
324         else {
325             query = tax.getCommonName();
326             qt = QUERY_TYPE.CN;
327             return obtainTaxonomy( TaxonomyDataManager.getCnTaxCacheMap(), query, qt );
328         }
329     }
330
331     public final static UniProtTaxonomy obtainUniProtTaxonomy( final String simple_name, QUERY_TYPE qt )
332             throws IOException, AncestralTaxonomyInferenceException {
333         if ( ForesterUtil.isEmpty( simple_name ) ) {
334             throw new IllegalArgumentException( "illegal attempt to use empty simple name" );
335         }
336         UniProtTaxonomy ut = null;
337         final String code = ParserUtils.extractTaxonomyCodeFromNodeName( simple_name,
338                                                                          NHXParser.TAXONOMY_EXTRACTION.AGGRESSIVE );
339         if ( !ForesterUtil.isEmpty( code ) ) {
340             qt = QUERY_TYPE.CODE;
341             ut = obtainTaxonomy( TaxonomyDataManager.getCodeTaxCacheMap(), code, qt );
342         }
343         if ( ut == null ) {
344             final String sn = ParserUtils.extractScientificNameFromNodeName( simple_name );
345             if ( !ForesterUtil.isEmpty( sn ) ) {
346                 qt = QUERY_TYPE.SN;
347                 ut = obtainTaxonomy( TaxonomyDataManager.getSnTaxCacheMap(), sn, qt );
348             }
349         }
350         if ( ut == null ) {
351             final String id = ParserUtils
352                     .extractUniprotTaxonomyIdFromNodeName( simple_name,
353                                                            NHXParser.TAXONOMY_EXTRACTION.PFAM_STYLE_RELAXED );
354             if ( !ForesterUtil.isEmpty( id ) ) {
355                 qt = QUERY_TYPE.ID;
356                 ut = obtainTaxonomy( TaxonomyDataManager.getIdTaxCacheMap(), id, qt );
357             }
358         }
359         if ( ut == null ) {
360             String sn = "";
361             final Matcher m = ParserUtils.TAXOMONY_SN_PATTERN_GENUS.matcher( simple_name );
362             if ( m.matches() ) {
363                 sn = m.group( 1 );
364             }
365             if ( !ForesterUtil.isEmpty( sn ) ) {
366                 qt = QUERY_TYPE.SN;
367                 ut = obtainTaxonomy( TaxonomyDataManager.getSnTaxCacheMap(), sn, qt );
368             }
369         }
370         return ut;
371     }
372
373     static final UniProtTaxonomy obtainUniProtTaxonomyFromLineage( final List<String> lineage )
374             throws AncestralTaxonomyInferenceException, IOException {
375         final String lineage_str = ForesterUtil.stringListToString( lineage, ">" );
376         if ( TaxonomyDataManager.getLineageTaxCacheMap().containsKey( lineage_str ) ) {
377             return TaxonomyDataManager.getLineageTaxCacheMap().get( lineage_str ).copy();
378         }
379         else {
380             final List<UniProtTaxonomy> matching_taxonomies = new ArrayList<UniProtTaxonomy>();
381             final List<UniProtTaxonomy> up_taxonomies = getTaxonomiesFromScientificName( lineage
382                                                                                          .get( lineage.size() - 1 ) );
383             if ( ( up_taxonomies != null ) && ( up_taxonomies.size() > 0 ) ) {
384                 for( final UniProtTaxonomy up_taxonomy : up_taxonomies ) {
385                     boolean match = true;
386                     I: for( int i = 0; i < lineage.size(); ++i ) {
387                         if ( ( i == up_taxonomy.getLineage().size() )
388                                 || !lineage.get( i ).equalsIgnoreCase( up_taxonomy.getLineage().get( i ) ) ) {
389                             match = false;
390                             break I;
391                         }
392                     }
393                     if ( match ) {
394                         matching_taxonomies.add( up_taxonomy );
395                     }
396                 }
397                 if ( matching_taxonomies.isEmpty() ) {
398                     throw new AncestralTaxonomyInferenceException( "lineage \""
399                             + ForesterUtil.stringListToString( lineage, " > " ) + "\" not found" );
400                 }
401                 //in case of more than one (e.g. "Xenopus" Genus and Subgenus), keep shorter, less specific  one:
402                 int shortest = Integer.MAX_VALUE;
403                 UniProtTaxonomy least_specific_up_tax = null;
404                 for( final UniProtTaxonomy m : matching_taxonomies ) {
405                     final int s = m.getLineage().size();
406                     if ( s < shortest ) {
407                         shortest = s;
408                         least_specific_up_tax = m;
409                     }
410                 }
411                 TaxonomyDataManager.getLineageTaxCacheMap().put( lineage_str, least_specific_up_tax );
412                 if ( !ForesterUtil.isEmpty( least_specific_up_tax.getScientificName() ) ) {
413                     TaxonomyDataManager.getSnTaxCacheMap().put( least_specific_up_tax.getScientificName(),
414                                                                 least_specific_up_tax );
415                 }
416                 if ( !ForesterUtil.isEmpty( least_specific_up_tax.getCode() ) ) {
417                     TaxonomyDataManager.getCodeTaxCacheMap().put( least_specific_up_tax.getCode(),
418                                                                   least_specific_up_tax );
419                 }
420                 if ( !ForesterUtil.isEmpty( least_specific_up_tax.getCommonName() ) ) {
421                     TaxonomyDataManager.getCnTaxCacheMap().put( least_specific_up_tax.getCommonName(),
422                                                                 least_specific_up_tax );
423                 }
424                 if ( !ForesterUtil.isEmpty( least_specific_up_tax.getId() ) ) {
425                     TaxonomyDataManager.getIdTaxCacheMap().put( least_specific_up_tax.getId(), least_specific_up_tax );
426                 }
427                 return least_specific_up_tax;
428             }
429             else {
430                 throw new AncestralTaxonomyInferenceException( "taxonomy \"" + ( lineage.get( lineage.size() - 1 ) )
431                                                                + "\" not found" );
432             }
433         }
434     }
435
436     synchronized final private static void updateTaxonomy( final QUERY_TYPE qt,
437                                                            final PhylogenyNode node,
438                                                            final Taxonomy tax,
439                                                            final UniProtTaxonomy up_tax )
440                                                                    throws PhyloXmlDataFormatException {
441         if ( ( qt != QUERY_TYPE.SN ) && !ForesterUtil.isEmpty( up_tax.getScientificName() )
442                 && ForesterUtil.isEmpty( tax.getScientificName() ) ) {
443             tax.setScientificName( up_tax.getScientificName() );
444         }
445         if ( node.isExternal() && ( qt != QUERY_TYPE.CODE ) && !ForesterUtil.isEmpty( up_tax.getCode() )
446                 && ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
447             tax.setTaxonomyCode( up_tax.getCode() );
448         }
449         if ( ( qt != QUERY_TYPE.CN ) && !ForesterUtil.isEmpty( up_tax.getCommonName() )
450                 && ForesterUtil.isEmpty( tax.getCommonName() ) ) {
451             tax.setCommonName( up_tax.getCommonName() );
452         }
453         if ( !ForesterUtil.isEmpty( up_tax.getSynonym() ) && !tax.getSynonyms().contains( up_tax.getSynonym() ) ) {
454             tax.getSynonyms().add( up_tax.getSynonym() );
455         }
456         if ( !ForesterUtil.isEmpty( up_tax.getRank() ) && ForesterUtil.isEmpty( tax.getRank() ) ) {
457             try {
458                 tax.setRank( up_tax.getRank().toLowerCase() );
459             }
460             catch ( final PhyloXmlDataFormatException ex ) {
461                 tax.setRank( "" );
462             }
463         }
464         if ( ( qt != QUERY_TYPE.ID ) && !ForesterUtil.isEmpty( up_tax.getId() )
465                 && ( ( tax.getIdentifier() == null ) || ForesterUtil.isEmpty( tax.getIdentifier().getValue() ) ) ) {
466             tax.setIdentifier( new Identifier( up_tax.getId(), "uniprot" ) );
467         }
468         if ( up_tax.getLineage() != null ) {
469             tax.setLineage( new ArrayList<String>() );
470             for( final String lin : up_tax.getLineage() ) {
471                 if ( !ForesterUtil.isEmpty( lin ) ) {
472                     tax.getLineage().add( lin );
473                 }
474             }
475         }
476     }
477
478     private final void execute() {
479         start( _mf, "taxonomy data" );
480         SortedSet<String> not_found = null;
481         try {
482             not_found = obtainDetailedTaxonomicInformation( _phy, _delete, _allow_simple_names );
483         }
484         catch ( final UnknownHostException e ) {
485             JOptionPane.showMessageDialog( _mf,
486                                            "Could not connect to \"" + getBaseUrl() + "\"",
487                                            "Network error during taxonomic information gathering",
488                                            JOptionPane.ERROR_MESSAGE );
489             return;
490         }
491         catch ( final IOException e ) {
492             e.printStackTrace();
493             JOptionPane.showMessageDialog( _mf,
494                                            e.toString(),
495                                            "Failed to obtain taxonomic information",
496                                            JOptionPane.ERROR_MESSAGE );
497             return;
498         }
499         catch ( final AncestralTaxonomyInferenceException e ) {
500             e.printStackTrace();
501             JOptionPane.showMessageDialog( _mf,
502                                            e.toString(),
503                                            "Failed to obtain taxonomic information",
504                                            JOptionPane.ERROR_MESSAGE );
505             return;
506         }
507         finally {
508             end( _mf );
509         }
510         if ( ( _phy == null ) || _phy.isEmpty() ) {
511             try {
512                 JOptionPane.showMessageDialog( _mf,
513                                                "None of the external node taxonomies could be resolved",
514                                                "Taxonomy Tool Failed",
515                                                JOptionPane.WARNING_MESSAGE );
516             }
517             catch ( final Exception e ) {
518                 // Not important if this fails, do nothing.
519             }
520             return;
521         }
522         _treepanel.setTree( _phy );
523         _mf.showWhole();
524         _treepanel.setEdited( true );
525         if ( ( not_found != null ) && ( not_found.size() > 0 ) ) {
526             int max = not_found.size();
527             boolean more = false;
528             if ( max > 20 ) {
529                 more = true;
530                 max = 20;
531             }
532             final StringBuffer sb = new StringBuffer();
533             sb.append( "Not all taxonomies could be resolved.\n" );
534             if ( not_found.size() == 1 ) {
535                 if ( _delete ) {
536                     sb.append( "The following taxonomy was not found and deleted (if external):\n" );
537                 }
538                 else {
539                     sb.append( "The following taxonomy was not found:\n" );
540                 }
541             }
542             else {
543                 if ( _delete ) {
544                     sb.append( "The following taxonomies were not found and deleted (if external) (total: "
545                             + not_found.size() + "):\n" );
546                 }
547                 else {
548                     sb.append( "The following taxonomies were not found (total: " + not_found.size() + "):\n" );
549                 }
550             }
551             int i = 0;
552             for( final String string : not_found ) {
553                 if ( i > 19 ) {
554                     break;
555                 }
556                 sb.append( string );
557                 sb.append( "\n" );
558                 ++i;
559             }
560             if ( more ) {
561                 sb.append( "..." );
562             }
563             try {
564                 JOptionPane.showMessageDialog( _mf,
565                                                sb.toString(),
566                                                "Taxonomy Tool Completed",
567                                                JOptionPane.WARNING_MESSAGE );
568             }
569             catch ( final Exception e ) {
570                 // Not important if this fails, do nothing.
571             }
572         }
573         else {
574             try {
575                 JOptionPane.showMessageDialog( _mf,
576                                                "Taxonomy tool successfully completed",
577                                                "Taxonomy Tool Completed",
578                                                JOptionPane.INFORMATION_MESSAGE );
579             }
580             catch ( final Exception e ) {
581                 // Not important if this fails, do nothing.
582             }
583         }
584     }
585
586     private final String getBaseUrl() {
587         return AncestralTaxonomyInferrer.getBaseUrl();
588     }
589
590     @Override
591     public void run() {
592         execute();
593     }
594 }