inprogress
[jalview.git] / forester / java / src / org / forester / surfacing / PrintableDomainSimilarity.java
1 // $Id:
2 //
3 // FORESTER -- software libraries and applications
4 // for evolutionary biology research and applications.
5 //
6 // Copyright (C) 2008-2009 Christian M. Zmasek
7 // Copyright (C) 2008-2009 Burnham Institute for Medical Research
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.surfacing;
28
29 import java.awt.Color;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.SortedMap;
33 import java.util.SortedSet;
34 import java.util.TreeMap;
35 import java.util.TreeSet;
36
37 import org.forester.phylogeny.Phylogeny;
38 import org.forester.phylogeny.PhylogenyNode;
39 import org.forester.species.Species;
40 import org.forester.surfacing.DomainSimilarityCalculator.Detailedness;
41 import org.forester.util.ForesterUtil;
42
43 public class PrintableDomainSimilarity implements DomainSimilarity {
44
45     final public static String                              SPECIES_SEPARATOR = "  ";
46     final private static int                                EQUAL             = 0;
47     final private static String                             NO_SPECIES        = "     ";
48     final private double                                    _min;
49     final private double                                    _max;
50     final private double                                    _mean;
51     final private double                                    _sd;
52     final private int                                       _n;
53     private final int                                       _max_difference_in_counts;
54     private final int                                       _max_difference;
55     final private CombinableDomains                         _combinable_domains;
56     final private SortedMap<Species, SpeciesSpecificDcData> _species_data;
57     private List<Species>                                   _species_order;
58     private DomainSimilarityCalculator.Detailedness         _detailedness;
59     private final boolean                                   _treat_as_binary_comparison;
60
61     public PrintableDomainSimilarity( final CombinableDomains combinable_domains,
62                                       final double min,
63                                       final double max,
64                                       final double mean,
65                                       final double median,
66                                       final double sd,
67                                       final int n,
68                                       final int max_difference_in_counts,
69                                       final int max_difference,
70                                       final SortedMap<Species, SpeciesSpecificDcData> species_data,
71                                       final boolean sort_by_species_count_first,
72                                       final boolean treat_as_binary_comparison ) {
73         if ( combinable_domains == null ) {
74             throw new IllegalArgumentException( "attempt to use null combinable domains" );
75         }
76         if ( species_data == null ) {
77             throw new IllegalArgumentException( "attempt to use null species data" );
78         }
79         if ( species_data.size() < 1 ) {
80             throw new IllegalArgumentException( "attempt to use empty species data" );
81         }
82         if ( n < 0 ) {
83             throw new IllegalArgumentException( "attempt to use N less than 0" );
84         }
85         if ( ( species_data.size() > 1 ) && ( n < 1 ) ) {
86             throw new IllegalArgumentException( "attempt to use N less than 1" );
87         }
88         if ( sd < 0.0 ) {
89             throw new IllegalArgumentException( "attempt to use negative SD" );
90         }
91         if ( max < min ) {
92             throw new IllegalArgumentException( "attempt to use max smaller than min" );
93         }
94         init();
95         _combinable_domains = combinable_domains;
96         _min = min;
97         _max = max;
98         _mean = mean;
99         _sd = sd;
100         _n = n;
101         _max_difference_in_counts = max_difference_in_counts;
102         _max_difference = max_difference;
103         _species_data = species_data;
104         _treat_as_binary_comparison = treat_as_binary_comparison;
105         final int s = species_data.size();
106         if ( ( ( s * s ) - s ) != ( getN() * 2 ) ) {
107             throw new IllegalArgumentException( "illegal species count and n: species count:" + s + ", n:" + _n
108                     + " for domain " + combinable_domains.getKeyDomain() );
109         }
110         if ( s > 2 ) {
111             if ( getMaximalDifferenceInCounts() < 0 ) {
112                 throw new IllegalArgumentException( "attempt to use negative max difference in counts with more than two species" );
113             }
114             if ( getMaximalDifference() < 0 ) {
115                 throw new IllegalArgumentException( "attempt to use negative max difference with more than two species" );
116             }
117         }
118     }
119
120     public PrintableDomainSimilarity( final CombinableDomains combinable_domains,
121                                       final int max_difference_in_counts,
122                                       final int max_difference,
123                                       final SortedMap<Species, SpeciesSpecificDcData> species_data,
124                                       final boolean sort_by_species_count_first,
125                                       final boolean treat_as_binary_comparison ) {
126         if ( combinable_domains == null ) {
127             throw new IllegalArgumentException( "attempt to use null combinable domains" );
128         }
129         if ( species_data == null ) {
130             throw new IllegalArgumentException( "attempt to use null species data" );
131         }
132         if ( species_data.size() < 1 ) {
133             throw new IllegalArgumentException( "attempt to use empty species data" );
134         }
135         init();
136         _combinable_domains = combinable_domains;
137         _min = -1;
138         _max = -1;
139         _mean = -1;
140         _sd = -1;
141         _n = -1;
142         _max_difference_in_counts = max_difference_in_counts;
143         _max_difference = max_difference;
144         _species_data = species_data;
145         _treat_as_binary_comparison = treat_as_binary_comparison;
146         final int s = species_data.size();
147         if ( s > 2 ) {
148             if ( getMaximalDifferenceInCounts() < 0 ) {
149                 throw new IllegalArgumentException( "attempt to use negative max difference in counts with more than two species" );
150             }
151             if ( getMaximalDifference() < 0 ) {
152                 throw new IllegalArgumentException( "attempt to use negative max difference with more than two species" );
153             }
154         }
155     }
156
157     private void addSpeciesSpecificDomainData( final StringBuffer sb,
158                                                final Species species,
159                                                final boolean html,
160                                                final Map<String, Integer> tax_code_to_id_map,
161                                                final Phylogeny phy ) {
162         if ( html ) {
163             addTaxWithLink( sb, species.getSpeciesId(), tax_code_to_id_map, phy );
164         }
165         else {
166             sb.append( species.getSpeciesId() );
167         }
168         if ( getDetaildness() != DomainSimilarityCalculator.Detailedness.BASIC ) {
169             if ( html ) {
170                 sb.append( ":" );
171             }
172             else {
173                 sb.append( "\t" );
174             }
175             sb.append( getSpeciesData().get( species ).toStringBuffer( getDetaildness(), html ) );
176         }
177         if ( html ) {
178             sb.append( "<br>" );
179         }
180         else {
181             sb.append( "\n\t" );
182         }
183     }
184
185     private void addTaxWithLink( final StringBuffer sb,
186                                  final String tax_code,
187                                  final Map<String, Integer> tax_code_to_id_map,
188                                  final Phylogeny phy ) {
189         Color c = null;
190         if ( phy != null && !phy.isEmpty() ) {
191             c = getColorDependingOnTaxonomy( tax_code, phy );
192         }
193         if ( c == null ) {
194             c = new Color( 0, 0, 0 );
195         }
196         final String hex = String.format( "#%02x%02x%02x", c.getRed(), c.getGreen(), c.getBlue() );
197         sb.append( "<b>" );
198         if ( !ForesterUtil.isEmpty( tax_code )
199                 && ( ( tax_code_to_id_map != null ) && tax_code_to_id_map.containsKey( tax_code ) ) ) {
200             sb.append( "<a href=\"" + SurfacingConstants.UNIPROT_TAXONOMY_ID_LINK + tax_code_to_id_map.get( tax_code )
201                     + "\" target=\"taxonomy_window\" color=\"" + hex + "\">" + tax_code + "</a>" );
202         }
203         else {
204             sb.append( tax_code );
205         }
206         sb.append( "</b>" );
207     }
208
209     private Color getColorDependingOnTaxonomy( final String tax_code, final Phylogeny phy ) {
210         List<PhylogenyNode> nodes = phy.getNodesViaTaxonomyCode( tax_code );
211         Color c = null;
212         if ( nodes == null || nodes.isEmpty() ) {
213             throw new RuntimeException( tax_code + " is not found" );
214         }
215         if ( nodes.size() != 1 ) {
216             throw new RuntimeException( tax_code + " is not unique" );
217         }
218         PhylogenyNode n = nodes.get( 0 );
219         while ( n != null ) {
220             c = null;
221             if ( n.getNodeData().isHasTaxonomy()
222                     && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
223                 c = SurfacingUtil.getColorForTaxCode( n.getNodeData().getTaxonomy().getScientificName() );
224             }
225             if ( c == null && !ForesterUtil.isEmpty( n.getName() ) ) {
226                 c = SurfacingUtil.getColorForTaxCode( n.getName() );
227             }
228             if ( c != null ) {
229                 break;
230             }
231             n = n.getParent();
232         }
233         return c;
234     }
235
236     private int compareByDomainId( final DomainSimilarity other ) {
237         return getDomainId().compareToIgnoreCase( other.getDomainId() );
238     }
239
240     @Override
241     public int compareTo( final DomainSimilarity domain_similarity ) {
242         if ( this == domain_similarity ) {
243             return EQUAL;
244         }
245         else if ( domain_similarity == null ) {
246             throw new IllegalArgumentException( "attempt to compare " + this.getClass() + " to null" );
247         }
248         else if ( domain_similarity.getClass() != this.getClass() ) {
249             throw new IllegalArgumentException( "attempt to compare " + this.getClass() + " to "
250                     + domain_similarity.getClass() );
251         }
252         return compareByDomainId( domain_similarity );
253     }
254
255     @Override
256     public SortedSet<String> getCombinableDomainIds( final Species species_of_combinable_domain ) {
257         final SortedSet<String> sorted_ids = new TreeSet<String>();
258         if ( getSpeciesData().containsKey( species_of_combinable_domain ) ) {
259             for( final String id : getSpeciesData().get( species_of_combinable_domain )
260                     .getCombinableDomainIdToCountsMap().keySet() ) {
261                 sorted_ids.add( id );
262             }
263         }
264         return sorted_ids;
265     }
266
267     private CombinableDomains getCombinableDomains() {
268         return _combinable_domains;
269     }
270
271     private DomainSimilarityCalculator.Detailedness getDetaildness() {
272         return _detailedness;
273     }
274
275     @Override
276     public String getDomainId() {
277         return getCombinableDomains().getKeyDomain();
278     }
279
280     @Override
281     public int getMaximalDifference() {
282         return _max_difference;
283     }
284
285     @Override
286     public int getMaximalDifferenceInCounts() {
287         return _max_difference_in_counts;
288     }
289
290     @Override
291     public double getMaximalSimilarityScore() {
292         return _max;
293     }
294
295     @Override
296     public double getMeanSimilarityScore() {
297         return _mean;
298     }
299
300     @Override
301     public double getMinimalSimilarityScore() {
302         return _min;
303     }
304
305     @Override
306     public int getN() {
307         return _n;
308     }
309
310     @Override
311     public SortedSet<Species> getSpecies() {
312         final SortedSet<Species> species = new TreeSet<Species>();
313         for( final Species s : getSpeciesData().keySet() ) {
314             species.add( s );
315         }
316         return species;
317     }
318
319     public List<Species> getSpeciesCustomOrder() {
320         return _species_order;
321     }
322
323     @Override
324     public SortedMap<Species, SpeciesSpecificDcData> getSpeciesData() {
325         return _species_data;
326     }
327
328     private StringBuffer getSpeciesDataInAlphabeticalOrder( final boolean html,
329                                                             final Map<String, Integer> tax_code_to_id_map,
330                                                             final Phylogeny phy ) {
331         final StringBuffer sb = new StringBuffer();
332         for( final Species species : getSpeciesData().keySet() ) {
333             addSpeciesSpecificDomainData( sb, species, html, tax_code_to_id_map, phy );
334         }
335         return sb;
336     }
337
338     private StringBuffer getDomainDataInAlphabeticalOrder() {
339         final SortedMap<String, SortedSet<String>> m = new TreeMap<String, SortedSet<String>>();
340         final StringBuffer sb = new StringBuffer();
341         for( final Species species : getSpeciesData().keySet() ) {
342             for( final String combable_dom : getCombinableDomainIds( species ) ) {
343                 if ( !m.containsKey( combable_dom ) ) {
344                     m.put( combable_dom, new TreeSet<String>() );
345                 }
346                 m.get( combable_dom ).add( species.getSpeciesId() );
347             }
348         }
349         for( final Map.Entry<String, SortedSet<String>> e : m.entrySet() ) {
350             sb.append( "<a href=\"" + SurfacingConstants.PFAM_FAMILY_ID_LINK + e.getKey() + "\">" + e.getKey() + "</a>" );
351             sb.append( ": " );
352             for( final String s : e.getValue() ) {
353                 sb.append( s );
354                 sb.append( " " );
355             }
356             sb.append( "<br>" );
357         }
358         return sb;
359     }
360
361     private StringBuffer getSpeciesDataInCustomOrder( final boolean html,
362                                                       final Map<String, Integer> tax_code_to_id_map,
363                                                       final Phylogeny phy ) {
364         final StringBuffer sb = new StringBuffer();
365         for( final Species order_species : getSpeciesCustomOrder() ) {
366             if ( getSpeciesData().keySet().contains( order_species ) ) {
367                 addSpeciesSpecificDomainData( sb, order_species, html, tax_code_to_id_map, phy );
368             }
369             else {
370                 sb.append( PrintableDomainSimilarity.NO_SPECIES );
371                 sb.append( PrintableDomainSimilarity.SPECIES_SEPARATOR );
372             }
373         }
374         return sb;
375     }
376
377     @Override
378     public double getStandardDeviationOfSimilarityScore() {
379         return _sd;
380     }
381
382     private void init() {
383         _detailedness = DomainSimilarityCalculator.Detailedness.PUNCTILIOUS;
384     }
385
386     private boolean isTreatAsBinaryComparison() {
387         return _treat_as_binary_comparison;
388     }
389
390     public void setDetailedness( final Detailedness detailedness ) {
391         _detailedness = detailedness;
392     }
393
394     public void setSpeciesOrder( final List<Species> species_order ) {
395         if ( !species_order.containsAll( getSpeciesData().keySet() ) ) {
396             throw new IllegalArgumentException( "list to order species must contain all species of multiple combinable domains similarity" );
397         }
398         _species_order = species_order;
399     }
400
401     @Override
402     public StringBuffer toStringBuffer( final PrintableDomainSimilarity.PRINT_OPTION print_option,
403                                         final Map<String, Integer> tax_code_to_id_map,
404                                         Phylogeny phy ) {
405         switch ( print_option ) {
406             case SIMPLE_TAB_DELIMITED:
407                 return toStringBufferSimpleTabDelimited();
408             case HTML:
409                 return toStringBufferDetailedHTML( tax_code_to_id_map, phy );
410             default:
411                 throw new AssertionError( "Unknown print option: " + print_option );
412         }
413     }
414
415     private StringBuffer toStringBufferDetailedHTML( final Map<String, Integer> tax_code_to_id_map, Phylogeny phy ) {
416         final StringBuffer sb = new StringBuffer();
417         sb.append( "<tr>" );
418         sb.append( "<td>" );
419         sb.append( "<b>" );
420         sb.append( "<a href=\"" + SurfacingConstants.PFAM_FAMILY_ID_LINK + getDomainId() + "\" target=\"pfam_window\">"
421                 + getDomainId() + "</a>" );
422         sb.append( "</b>" );
423         sb.append( "<a name=\"" + getDomainId() + "\">" );
424         sb.append( "</td>" );
425         sb.append( "<td>" );
426         sb.append( "<a href=\"" + SurfacingConstants.GOOGLE_SCHOLAR_SEARCH + getDomainId()
427                 + "\" target=\"gs_window\">gs</a>" );
428         sb.append( "</td>" );
429         if ( getMaximalSimilarityScore() > 0 ) {
430             sb.append( "<td>" );
431             sb.append( ForesterUtil.round( getMeanSimilarityScore(), 3 ) );
432             sb.append( "</td>" );
433             if ( SurfacingConstants.PRINT_MORE_DOM_SIMILARITY_INFO ) {
434                 if ( !isTreatAsBinaryComparison() ) {
435                     sb.append( "<td>" );
436                     sb.append( "(" );
437                     sb.append( ForesterUtil.round( getStandardDeviationOfSimilarityScore(), 3 ) );
438                     sb.append( ")" );
439                     sb.append( "</td>" );
440                     sb.append( "<td>" );
441                     sb.append( "[" );
442                     sb.append( ForesterUtil.round( getMinimalSimilarityScore(), 3 ) );
443                     sb.append( "-" );
444                     sb.append( ForesterUtil.round( getMaximalSimilarityScore(), 3 ) );
445                     sb.append( "]" );
446                     sb.append( "</td>" );
447                 }
448             }
449         }
450         sb.append( "<td>" );
451         sb.append( getMaximalDifference() );
452         sb.append( "</td>" );
453         sb.append( "<td>" );
454         if ( isTreatAsBinaryComparison() ) {
455             sb.append( getMaximalDifferenceInCounts() );
456         }
457         else {
458             sb.append( Math.abs( getMaximalDifferenceInCounts() ) );
459         }
460         sb.append( "</td>" );
461         if ( !isTreatAsBinaryComparison() ) {
462             sb.append( "<td>" );
463             sb.append( "<b>" );
464             sb.append( getSpeciesData().size() );
465             sb.append( "</b>" );
466             sb.append( "</td>" );
467         }
468         if ( ( getSpeciesCustomOrder() == null ) || getSpeciesCustomOrder().isEmpty() ) {
469             sb.append( "<td>" );
470             sb.append( getSpeciesDataInAlphabeticalOrder( true, tax_code_to_id_map, phy ) );
471             sb.append( getDomainDataInAlphabeticalOrder() );
472             sb.append( "</td>" );
473         }
474         else {
475             sb.append( "<td>" );
476             sb.append( getSpeciesDataInCustomOrder( true, tax_code_to_id_map, phy ) );
477             sb.append( getDomainDataInAlphabeticalOrder() );
478             sb.append( "</td>" );
479         }
480         sb.append( "</tr>" );
481         return sb;
482     }
483
484     private StringBuffer toStringBufferSimpleTabDelimited() {
485         final StringBuffer sb = new StringBuffer();
486         sb.append( getDomainId() );
487         sb.append( "\t" );
488         sb.append( getSpeciesDataInAlphabeticalOrder( false, null, null ) );
489         sb.append( "\n" );
490         return sb;
491     }
492
493     public static enum PRINT_OPTION {
494         SIMPLE_TAB_DELIMITED, HTML;
495     }
496 }