JAL-2797 added constructor including embedded/standalone boolean
[jalview.git] / forester / java / src / org / forester / analysis / AncestralTaxonomyInference.java
1 // forester -- software libraries and applications
2 // for genomics and evolutionary biology research.
3 //
4 // Copyright (C) 2010 Christian M Zmasek
5 // Copyright (C) 2010 Sanford-Burnham Medical Research Institute
6 // All rights reserved
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Lesser General Public
10 // License as published by the Free Software Foundation; either
11 // version 2.1 of the License, or (at your option) any later version.
12 //
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Lesser General Public License for more details.
17 //
18 // You should have received a copy of the GNU Lesser General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
21 //
22 // Contact: phylosoft @ gmail . com
23 // WWW: https://sites.google.com/site/cmzmasek/home/software/forester
24
25 package org.forester.analysis;
26
27 import java.io.IOException;
28 import java.util.ArrayList;
29 import java.util.List;
30
31 import org.forester.io.parsers.phyloxml.PhyloXmlDataFormatException;
32 import org.forester.phylogeny.Phylogeny;
33 import org.forester.phylogeny.PhylogenyNode;
34 import org.forester.phylogeny.data.Identifier;
35 import org.forester.phylogeny.data.Taxonomy;
36 import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
37 import org.forester.util.ForesterUtil;
38 import org.forester.ws.seqdb.UniProtTaxonomy;
39
40 public final class AncestralTaxonomyInference {
41
42     public static void inferTaxonomyFromDescendents( final Phylogeny phy ) throws IOException,
43     AncestralTaxonomyInferenceException {
44         TaxonomyDataManager.clearCachesIfTooLarge();
45         for( final PhylogenyNodeIterator iter = phy.iteratorPostorder(); iter.hasNext(); ) {
46             final PhylogenyNode node = iter.next();
47             if ( !node.isExternal() ) {
48                 inferTaxonomyFromDescendents( node );
49             }
50         }
51     }
52
53     private static void inferTaxonomyFromDescendents( final PhylogenyNode n ) throws IOException,
54     AncestralTaxonomyInferenceException {
55         if ( n.isExternal() ) {
56             throw new IllegalArgumentException( "attempt to infer taxonomy from descendants of external node" );
57         }
58         n.getNodeData().setTaxonomy( null );
59         final List<PhylogenyNode> descs = n.getDescendants();
60         final List<String[]> lineages = new ArrayList<String[]>();
61         int shortest_lin_length = Integer.MAX_VALUE;
62         for( final PhylogenyNode desc : descs ) {
63             if ( desc.getNodeData().isHasTaxonomy()
64                     && ( TaxonomyDataManager.isHasAppropriateId( desc.getNodeData().getTaxonomy() )
65                             || !ForesterUtil.isEmpty( desc.getNodeData().getTaxonomy().getScientificName() )
66                             || !ForesterUtil.isEmpty( desc.getNodeData().getTaxonomy().getLineage() )
67                             || !ForesterUtil.isEmpty( desc.getNodeData().getTaxonomy().getTaxonomyCode() ) || !ForesterUtil
68                             .isEmpty( desc.getNodeData().getTaxonomy().getCommonName() ) ) ) {
69                 final UniProtTaxonomy up_tax = TaxonomyDataManager.obtainUniProtTaxonomy( desc.getNodeData()
70                                                                                           .getTaxonomy(), null, null );
71                 if ( ( up_tax == null ) && ForesterUtil.isEmpty( desc.getNodeData().getTaxonomy().getLineage() ) ) {
72                     String desc_str = "";
73                     if ( !ForesterUtil.isEmpty( desc.getName() ) ) {
74                         desc_str = "\"" + desc.getName() + "\"";
75                     }
76                     else {
77                         desc_str = "[" + desc.getId() + "]";
78                     }
79                     System.out.println( desc.getNodeData().getTaxonomy().toString() );
80                     System.out.println( ForesterUtil.stringListToString( desc.getNodeData().getTaxonomy().getLineage(),
81                             "  >  " ) );
82                     throw new AncestralTaxonomyInferenceException( "a taxonomy for node " + desc_str
83                                                                    + " could not be established from the database" );
84                 }
85                 String[] lineage = ForesterUtil.stringListToArray( desc.getNodeData().getTaxonomy().getLineage() );
86                 if ( ( lineage == null ) || ( lineage.length < 1 ) ) {
87                     lineage = ForesterUtil.stringListToArray( up_tax.getLineage() );
88                 }
89                 if ( ( lineage == null ) || ( lineage.length < 1 ) ) {
90                     throw new AncestralTaxonomyInferenceException( "a taxonomic lineage for node \""
91                             + desc.getNodeData().getTaxonomy().toString() + "\" could not be established" );
92                 }
93                 if ( lineage.length < shortest_lin_length ) {
94                     shortest_lin_length = lineage.length;
95                 }
96                 lineages.add( lineage );
97             }
98             else {
99                 String node = "";
100                 if ( !ForesterUtil.isEmpty( desc.getName() ) ) {
101                     node = "\"" + desc.getName() + "\"";
102                 }
103                 else {
104                     node = "[" + desc.getId() + "]";
105                 }
106                 throw new AncestralTaxonomyInferenceException( "node " + node
107                                                                + " has no or inappropriate taxonomic information" );
108             }
109         }
110         final List<String> last_common_lineage = new ArrayList<String>();
111         String last_common = null;
112         if ( shortest_lin_length > 0 ) {
113             I: for( int i = 0; i < shortest_lin_length; ++i ) {
114                 final String lineage_0 = lineages.get( 0 )[ i ];
115                 for( int j = 1; j < lineages.size(); ++j ) {
116                     if ( !lineage_0.equals( lineages.get( j )[ i ] ) ) {
117                         break I;
118                     }
119                 }
120                 last_common_lineage.add( lineage_0 );
121                 last_common = lineage_0;
122             }
123         }
124         if ( last_common_lineage.isEmpty() ) {
125             boolean saw_viruses = false;
126             boolean saw_cellular_organism = false;
127             boolean saw_x = false;
128             for( final String[] lineage : lineages ) {
129                 if ( lineage.length > 0 ) {
130                     if ( lineage[ 0 ].equalsIgnoreCase( UniProtTaxonomy.VIRUSES ) ) {
131                         saw_viruses = true;
132                     }
133                     else if ( lineage[ 0 ].equalsIgnoreCase( UniProtTaxonomy.CELLULAR_ORGANISMS ) ) {
134                         saw_cellular_organism = true;
135                     }
136                     else if ( lineage[ 0 ].equalsIgnoreCase( UniProtTaxonomy.X ) ) {
137                         saw_x = true;
138                     }
139                     if ( ( saw_cellular_organism && saw_viruses ) || saw_x ) {
140                         break;
141                     }
142                 }
143             }
144             if ( ( saw_cellular_organism && saw_viruses ) || saw_x ) {
145                 last_common_lineage.add( UniProtTaxonomy.X );
146                 last_common = UniProtTaxonomy.X;
147             }
148             else {
149                 String msg = "no common lineage for:\n";
150                 int counter = 0;
151                 for( final String[] strings : lineages ) {
152                     msg += counter + ": ";
153                     ++counter;
154                     for( final String string : strings ) {
155                         msg += string + " ";
156                     }
157                     msg += "\n";
158                 }
159                 throw new AncestralTaxonomyInferenceException( msg );
160             }
161         }
162         final Taxonomy tax = new Taxonomy();
163         n.getNodeData().setTaxonomy( tax );
164         tax.setScientificName( last_common );
165         final UniProtTaxonomy up_tax = TaxonomyDataManager.obtainUniProtTaxonomyFromLineage( last_common_lineage );
166         if ( up_tax != null ) {
167             if ( !ForesterUtil.isEmpty( up_tax.getRank() ) ) {
168                 try {
169                     tax.setRank( up_tax.getRank().toLowerCase() );
170                 }
171                 catch ( final PhyloXmlDataFormatException ex ) {
172                     tax.setRank( "" );
173                 }
174             }
175             if ( !ForesterUtil.isEmpty( up_tax.getId() ) ) {
176                 tax.setIdentifier( new Identifier( up_tax.getId(), "uniprot" ) );
177             }
178             if ( !ForesterUtil.isEmpty( up_tax.getCommonName() ) ) {
179                 tax.setCommonName( up_tax.getCommonName() );
180             }
181             if ( !ForesterUtil.isEmpty( up_tax.getSynonym() ) && !tax.getSynonyms().contains( up_tax.getSynonym() ) ) {
182                 tax.getSynonyms().add( up_tax.getSynonym() );
183             }
184             if ( up_tax.getLineage() != null ) {
185                 tax.setLineage( new ArrayList<String>() );
186                 for( final String lin : up_tax.getLineage() ) {
187                     if ( !ForesterUtil.isEmpty( lin ) ) {
188                         tax.getLineage().add( lin );
189                     }
190                 }
191             }
192         }
193         if ( ForesterUtil.isEmpty( tax.getLineage() ) ) {
194             tax.setLineage( new ArrayList<String>() );
195             for( final String lin : last_common_lineage ) {
196                 if ( !ForesterUtil.isEmpty( lin ) ) {
197                     tax.getLineage().add( lin );
198                 }
199             }
200         }
201         for( final PhylogenyNode desc : descs ) {
202             if ( !desc.isExternal() && desc.getNodeData().isHasTaxonomy()
203                     && desc.getNodeData().getTaxonomy().isEqual( tax ) ) {
204                 desc.getNodeData().setTaxonomy( null );
205             }
206         }
207     }
208 }