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