627e1523c31579758399dbe04b893f4a52f9d9e1
[jalview.git] / forester / java / src / org / forester / archaeopteryx / AptxUtil.java
1 // $Id:
2 // FORESTER -- software libraries and applications
3 // for evolutionary biology research and applications.
4 //
5 // Copyright (C) 2008-2009 Christian M. Zmasek
6 // Copyright (C) 2008-2009 Burnham Institute for Medical Research
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.archaeopteryx;
27
28 import java.awt.Color;
29 import java.awt.Component;
30 import java.awt.Graphics2D;
31 import java.awt.GraphicsEnvironment;
32 import java.awt.Rectangle;
33 import java.awt.RenderingHints;
34 import java.awt.image.BufferedImage;
35 import java.io.ByteArrayOutputStream;
36 import java.io.File;
37 import java.io.FileNotFoundException;
38 import java.io.IOException;
39 import java.lang.reflect.InvocationTargetException;
40 import java.lang.reflect.Method;
41 import java.net.URI;
42 import java.net.URL;
43 import java.text.ParseException;
44 import java.util.Arrays;
45 import java.util.HashMap;
46 import java.util.HashSet;
47 import java.util.Iterator;
48 import java.util.List;
49 import java.util.Locale;
50 import java.util.Map;
51 import java.util.Set;
52 import java.util.SortedSet;
53 import java.util.TreeSet;
54 import java.util.regex.Matcher;
55 import java.util.regex.Pattern;
56
57 import javax.imageio.IIOImage;
58 import javax.imageio.ImageIO;
59 import javax.imageio.ImageWriteParam;
60 import javax.imageio.ImageWriter;
61 import javax.imageio.stream.ImageOutputStream;
62 import javax.swing.JApplet;
63 import javax.swing.JOptionPane;
64 import javax.swing.text.MaskFormatter;
65
66 import org.forester.analysis.TaxonomyDataManager;
67 import org.forester.io.parsers.PhylogenyParser;
68 import org.forester.io.parsers.nexus.NexusPhylogeniesParser;
69 import org.forester.io.parsers.nhx.NHXParser;
70 import org.forester.io.parsers.nhx.NHXParser.TAXONOMY_EXTRACTION;
71 import org.forester.io.parsers.phyloxml.PhyloXmlUtil;
72 import org.forester.io.parsers.tol.TolParser;
73 import org.forester.io.parsers.util.ParserUtils;
74 import org.forester.phylogeny.Phylogeny;
75 import org.forester.phylogeny.PhylogenyMethods;
76 import org.forester.phylogeny.PhylogenyNode;
77 import org.forester.phylogeny.data.Accession;
78 import org.forester.phylogeny.data.BranchColor;
79 import org.forester.phylogeny.data.Taxonomy;
80 import org.forester.phylogeny.factories.ParserBasedPhylogenyFactory;
81 import org.forester.phylogeny.factories.PhylogenyFactory;
82 import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
83 import org.forester.phylogeny.iterators.PreorderTreeIterator;
84 import org.forester.util.AsciiHistogram;
85 import org.forester.util.DescriptiveStatistics;
86 import org.forester.util.ForesterUtil;
87 import org.forester.ws.seqdb.UniProtTaxonomy;
88
89 public final class AptxUtil {
90
91     final static String           UNIPROT_KB                     = "http://www.uniprot.org/uniprot/";
92     final static Pattern          UNIPROT_KB_PATTERN_1             = Pattern
93             .compile( "\\bsp|tr.\\S([A-Z0-9]{5,6})\\b" );
94
95     final static Pattern          UNIPROT_KB_PATTERN_2             = Pattern
96                                                                          .compile( "\\b[A-Z0-9]{5,6}_[A-Z9][A-Z]{2}[A-Z0-9]{2}|RAT|PIG|PEA\\b" );
97     private final static Pattern  seq_identifier_pattern_1       = Pattern
98                                                                          .compile( "^([A-Za-z]{2,5})[|=:]([0-9A-Za-z_\\.]{5,40})\\s*$" );
99     private final static Pattern  seq_identifier_pattern_2       = Pattern
100                                                                          .compile( "^([A-Za-z]{2,5})[|=:]([0-9A-Za-z_\\.]{5,40})[|,; ].*$" );
101     private final static String[] AVAILABLE_FONT_FAMILIES_SORTED = GraphicsEnvironment.getLocalGraphicsEnvironment()
102                                                                          .getAvailableFontFamilyNames();
103     static {
104         Arrays.sort( AVAILABLE_FONT_FAMILIES_SORTED );
105     }
106
107     public static MaskFormatter createMaskFormatter( final String s ) {
108         MaskFormatter formatter = null;
109         try {
110             formatter = new MaskFormatter( s );
111         }
112         catch ( final ParseException e ) {
113             throw new IllegalArgumentException( e );
114         }
115         return formatter;
116     }
117
118     final static public boolean isHasAtLeastNodeWithEvent( final Phylogeny phy ) {
119         final PhylogenyNodeIterator it = phy.iteratorPostorder();
120         while ( it.hasNext() ) {
121             if ( it.next().getNodeData().isHasEvent() ) {
122                 return true;
123             }
124         }
125         return false;
126     }
127
128     /**
129      * Returns true if at least one branch has a length larger than zero.
130      * 
131      * 
132      * @param phy
133      */
134     final static public boolean isHasAtLeastOneBranchLengthLargerThanZero( final Phylogeny phy ) {
135         final PhylogenyNodeIterator it = phy.iteratorPostorder();
136         while ( it.hasNext() ) {
137             if ( it.next().getDistanceToParent() > 0.0 ) {
138                 return true;
139             }
140         }
141         return false;
142     }
143
144     final static public boolean isHasAtLeastOneBranchWithSupportValues( final Phylogeny phy ) {
145         final PhylogenyNodeIterator it = phy.iteratorPostorder();
146         while ( it.hasNext() ) {
147             if ( it.next().getBranchData().isHasConfidences() ) {
148                 return true;
149             }
150         }
151         return false;
152     }
153
154     final public static void launchWebBrowser( final URI uri,
155                                                final boolean is_applet,
156                                                final JApplet applet,
157                                                final String frame_name ) throws IOException {
158         if ( is_applet ) {
159             applet.getAppletContext().showDocument( uri.toURL(), frame_name );
160         }
161         else {
162             // This requires Java 1.6:
163             // =======================
164             // boolean no_desktop = false;
165             // try {
166             // if ( Desktop.isDesktopSupported() ) {
167             // System.out.println( "desktop supported" );
168             // final Desktop dt = Desktop.getDesktop();
169             // dt.browse( uri );
170             // }
171             // else {
172             // no_desktop = true;
173             // }
174             // }
175             // catch ( final Exception ex ) {
176             // ex.printStackTrace();
177             // no_desktop = true;
178             // }
179             // catch ( final Error er ) {
180             // er.printStackTrace();
181             // no_desktop = true;
182             // }
183             // if ( no_desktop ) {
184             // System.out.println( "desktop not supported" );
185             try {
186                 openUrlInWebBrowser( uri.toString() );
187             }
188             catch ( final Exception e ) {
189                 throw new IOException( e );
190             }
191             // }
192         }
193     }
194
195     public static Set<Taxonomy> obtainAllDistinctTaxonomies( final PhylogenyNode node ) {
196         final List<PhylogenyNode> descs = node.getAllExternalDescendants();
197         final Set<Taxonomy> tax_set = new HashSet<Taxonomy>();
198         for( final PhylogenyNode n : descs ) {
199             if ( n.getNodeData().isHasTaxonomy() && !n.getNodeData().getTaxonomy().isEmpty() ) {
200                 tax_set.add( n.getNodeData().getTaxonomy() );
201             }
202         }
203         return tax_set;
204     }
205
206     /**
207      * Returns the set of distinct taxonomies of
208      * all external nodes of node.
209      * If at least one the external nodes has no taxonomy,
210      * null is returned.
211      * 
212      */
213     public static Set<Taxonomy> obtainDistinctTaxonomies( final PhylogenyNode node ) {
214         final List<PhylogenyNode> descs = node.getAllExternalDescendants();
215         final Set<Taxonomy> tax_set = new HashSet<Taxonomy>();
216         for( final PhylogenyNode n : descs ) {
217             if ( !n.getNodeData().isHasTaxonomy() || n.getNodeData().getTaxonomy().isEmpty() ) {
218                 return null;
219             }
220             tax_set.add( n.getNodeData().getTaxonomy() );
221         }
222         return tax_set;
223     }
224
225     public final static Accession obtainSequenceAccessionFromName( final String sequence_name ) {
226         final String n = sequence_name.trim();
227         final Matcher matcher1 = seq_identifier_pattern_1.matcher( n );
228         String group1 = "";
229         String group2 = "";
230         if ( matcher1.matches() ) {
231             group1 = matcher1.group( 1 );
232             group2 = matcher1.group( 2 );
233         }
234         else {
235             final Matcher matcher2 = seq_identifier_pattern_2.matcher( n );
236             if ( matcher2.matches() ) {
237                 group1 = matcher2.group( 1 );
238                 group2 = matcher2.group( 2 );
239             }
240         }
241         if ( ForesterUtil.isEmpty( group1 ) || ForesterUtil.isEmpty( group2 ) ) {
242             return null;
243         }
244         return new Accession( group2, group1 );
245     }
246
247     public final static void printWarningMessage( final String name, final String message ) {
248         System.out.println( "[" + name + "] > " + message );
249     }
250
251     final public static void showErrorMessage( final Component parent, final String error_msg ) {
252         printAppletMessage( Constants.PRG_NAME, error_msg );
253         JOptionPane.showMessageDialog( parent, error_msg, "[" + Constants.PRG_NAME + " " + Constants.VERSION
254                 + "] Error", JOptionPane.ERROR_MESSAGE );
255     }
256
257     public final static void showExtDescNodeDataUserSelectedHelper( final ControlPanel cp,
258                                                                     final PhylogenyNode node,
259                                                                     final List<String> data ) {
260         final StringBuilder sb = new StringBuilder();
261         if ( cp.isShowNodeNames() && !ForesterUtil.isEmpty( node.getName() ) ) {
262             showExtDescNodeDataUserSelectedHelperHelper( node.getName(), sb );
263         }
264         if ( cp.isShowGeneNames() && node.getNodeData().isHasSequence()
265                 && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getName() ) ) {
266             showExtDescNodeDataUserSelectedHelperHelper( node.getNodeData().getSequence().getName(), sb );
267         }
268         if ( cp.isShowGeneSymbols() && node.getNodeData().isHasSequence()
269                 && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getSymbol() ) ) {
270             showExtDescNodeDataUserSelectedHelperHelper( node.getNodeData().getSequence().getSymbol(), sb );
271         }
272         if ( cp.isShowSequenceAcc() && node.getNodeData().isHasSequence()
273                 && ( node.getNodeData().getSequence().getAccession() != null )
274                 && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().toString() ) ) {
275             showExtDescNodeDataUserSelectedHelperHelper( node.getNodeData().getSequence().getAccession().toString(), sb );
276         }
277         if ( cp.isShowTaxonomyCode() && node.getNodeData().isHasTaxonomy()
278                 && !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
279             showExtDescNodeDataUserSelectedHelperHelper( node.getNodeData().getTaxonomy().getTaxonomyCode(), sb );
280         }
281         if ( cp.isShowTaxonomyScientificNames() && node.getNodeData().isHasTaxonomy()
282                 && !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getScientificName() ) ) {
283             showExtDescNodeDataUserSelectedHelperHelper( node.getNodeData().getTaxonomy().getScientificName(), sb );
284         }
285         if ( ( cp.isShowGeneNames() || cp.isShowGeneSymbols() || cp.isShowSequenceAcc() )
286                 && node.getNodeData().isHasSequence()
287                 && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getMolecularSequence() ) ) {
288             showExtDescNodeDataUserSelectedHelperHelper( node.getNodeData().getSequence().getMolecularSequence(), sb );
289         }
290         final String s = sb.toString().trim();
291         if ( !ForesterUtil.isEmpty( s ) ) {
292             data.add( s );
293         }
294     }
295
296     public final static void showExtDescNodeDataUserSelectedHelperHelper( final String s, final StringBuilder sb ) {
297         if ( sb.length() > 0 ) {
298             sb.append( "\t" );
299         }
300         sb.append( s );
301     }
302
303     final public static void showInformationMessage( final Component parent, final String title, final String msg ) {
304         JOptionPane.showMessageDialog( parent, msg, title, JOptionPane.INFORMATION_MESSAGE );
305     }
306
307     public static void writePhylogenyToGraphicsFile( final File intree,
308                                                      final File outfile,
309                                                      final int width,
310                                                      final int height,
311                                                      final GraphicsExportType type,
312                                                      final Configuration config ) throws IOException {
313         final PhylogenyParser parser = ParserUtils.createParserDependingOnFileType( intree, true );
314         Phylogeny[] phys = null;
315         phys = PhylogenyMethods.readPhylogenies( parser, intree );
316         writePhylogenyToGraphicsFile( phys[ 0 ], outfile, width, height, type, config );
317     }
318
319     public static void writePhylogenyToGraphicsFile( final Phylogeny phy,
320                                                      final File outfile,
321                                                      final int width,
322                                                      final int height,
323                                                      final GraphicsExportType type,
324                                                      final Configuration config ) throws IOException {
325         final Phylogeny[] phys = new Phylogeny[ 1 ];
326         phys[ 0 ] = phy;
327         final MainFrameApplication mf = MainFrameApplication.createInstance( phys, config );
328         AptxUtil.writePhylogenyToGraphicsFileNonInteractive( outfile, width, height, mf.getMainPanel()
329                 .getCurrentTreePanel(), mf.getMainPanel().getControlPanel(), type, mf.getOptions() );
330         mf.end();
331     }
332
333     public final static void writePhylogenyToGraphicsFileNonInteractive( final File outfile,
334                                                                          final int width,
335                                                                          final int height,
336                                                                          final TreePanel tree_panel,
337                                                                          final ControlPanel ac,
338                                                                          final GraphicsExportType type,
339                                                                          final Options options ) throws IOException {
340         tree_panel.calcParametersForPainting( width, height, true );
341         tree_panel.resetPreferredSize();
342         tree_panel.repaint();
343         final RenderingHints rendering_hints = new RenderingHints( RenderingHints.KEY_RENDERING,
344                                                                    RenderingHints.VALUE_RENDER_QUALITY );
345         rendering_hints.put( RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY );
346         if ( options.isAntialiasPrint() ) {
347             rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
348             rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
349         }
350         else {
351             rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF );
352             rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
353         }
354         final Phylogeny phylogeny = tree_panel.getPhylogeny();
355         if ( ( phylogeny == null ) || phylogeny.isEmpty() ) {
356             return;
357         }
358         if ( outfile.isDirectory() ) {
359             throw new IOException( "\"" + outfile + "\" is a directory" );
360         }
361         final BufferedImage buffered_img = new BufferedImage( width, height, BufferedImage.TYPE_INT_RGB );
362         final Graphics2D g2d = buffered_img.createGraphics();
363         g2d.setRenderingHints( rendering_hints );
364         tree_panel.paintPhylogeny( g2d, false, true, width, height, 0, 0 );
365         if ( type == GraphicsExportType.TIFF ) {
366             writeToTiff( outfile, buffered_img );
367         }
368         else {
369             ImageIO.write( buffered_img, type.toString(), outfile );
370         }
371         g2d.dispose();
372     }
373
374     final static void addPhylogeniesToTabs( final Phylogeny[] phys,
375                                             final String default_name,
376                                             final String full_path,
377                                             final Configuration configuration,
378                                             final MainPanel main_panel ) {
379         if ( phys.length > Constants.MAX_TREES_TO_LOAD ) {
380             JOptionPane.showMessageDialog( main_panel, "Attempt to load " + phys.length
381                     + " phylogenies,\ngoing to load only the first " + Constants.MAX_TREES_TO_LOAD, Constants.PRG_NAME
382                     + " more than " + Constants.MAX_TREES_TO_LOAD + " phylogenies", JOptionPane.WARNING_MESSAGE );
383         }
384         int i = 1;
385         for( final Phylogeny phy : phys ) {
386             if ( !phy.isEmpty() ) {
387                 if ( i <= Constants.MAX_TREES_TO_LOAD ) {
388                     String my_name = "";
389                     String my_name_for_file = "";
390                     if ( phys.length > 1 ) {
391                         if ( !ForesterUtil.isEmpty( default_name ) ) {
392                             my_name = new String( default_name );
393                         }
394                         if ( !ForesterUtil.isEmpty( full_path ) ) {
395                             my_name_for_file = new String( full_path );
396                         }
397                         else if ( !ForesterUtil.isEmpty( default_name ) ) {
398                             my_name_for_file = new String( default_name );
399                         }
400                         String suffix = "";
401                         if ( my_name_for_file.indexOf( '.' ) > 0 ) {
402                             suffix = my_name_for_file.substring( my_name_for_file.lastIndexOf( '.' ),
403                                                                  my_name_for_file.length() );
404                             my_name_for_file = my_name_for_file.substring( 0, my_name_for_file.lastIndexOf( '.' ) );
405                         }
406                         if ( !ForesterUtil.isEmpty( my_name_for_file ) ) {
407                             my_name_for_file += "_";
408                         }
409                         if ( !ForesterUtil.isEmpty( phy.getName() ) ) {
410                             my_name_for_file += phy.getName().replaceAll( " ", "_" );
411                         }
412                         else if ( phy.getIdentifier() != null ) {
413                             final StringBuffer sb = new StringBuffer();
414                             if ( !ForesterUtil.isEmpty( phy.getIdentifier().getProvider() ) ) {
415                                 sb.append( phy.getIdentifier().getProvider() );
416                                 sb.append( "_" );
417                             }
418                             sb.append( phy.getIdentifier().getValue() );
419                             my_name_for_file += sb;
420                         }
421                         else {
422                             my_name_for_file += i;
423                         }
424                         if ( !ForesterUtil.isEmpty( my_name ) && ForesterUtil.isEmpty( phy.getName() )
425                                 && ( phy.getIdentifier() == null ) ) {
426                             my_name = my_name + " [" + i + "]";
427                         }
428                         if ( !ForesterUtil.isEmpty( suffix ) ) {
429                             my_name_for_file += suffix;
430                         }
431                     }
432                     else {
433                         if ( !ForesterUtil.isEmpty( default_name ) ) {
434                             my_name = new String( default_name );
435                         }
436                         my_name_for_file = "";
437                         if ( !ForesterUtil.isEmpty( full_path ) ) {
438                             my_name_for_file = new String( full_path );
439                         }
440                         else if ( !ForesterUtil.isEmpty( default_name ) ) {
441                             my_name_for_file = new String( default_name );
442                         }
443                         if ( ForesterUtil.isEmpty( my_name_for_file ) ) {
444                             if ( !ForesterUtil.isEmpty( phy.getName() ) ) {
445                                 my_name_for_file = new String( phy.getName() ).replaceAll( " ", "_" );
446                             }
447                             else if ( phy.getIdentifier() != null ) {
448                                 final StringBuffer sb = new StringBuffer();
449                                 if ( !ForesterUtil.isEmpty( phy.getIdentifier().getProvider() ) ) {
450                                     sb.append( phy.getIdentifier().getProvider() );
451                                     sb.append( "_" );
452                                 }
453                                 sb.append( phy.getIdentifier().getValue() );
454                                 my_name_for_file = new String( sb.toString().replaceAll( " ", "_" ) );
455                             }
456                         }
457                     }
458                     main_panel.addPhylogenyInNewTab( phy, configuration, my_name, full_path );
459                     main_panel.getCurrentTreePanel().setTreeFile( new File( my_name_for_file ) );
460                     lookAtSomeTreePropertiesForAptxControlSettings( phy, main_panel.getControlPanel(), configuration );
461                     ++i;
462                 }
463             }
464         }
465     }
466
467     final static void addPhylogenyToPanel( final Phylogeny[] phys,
468                                            final Configuration configuration,
469                                            final MainPanel main_panel ) {
470         final Phylogeny phy = phys[ 0 ];
471         main_panel.addPhylogenyInPanel( phy, configuration );
472         lookAtSomeTreePropertiesForAptxControlSettings( phy, main_panel.getControlPanel(), configuration );
473     }
474
475     final static Color calculateColorFromString( final String str ) {
476         final String species_uc = str.toUpperCase();
477         char first = species_uc.charAt( 0 );
478         char second = ' ';
479         char third = ' ';
480         if ( species_uc.length() > 1 ) {
481             second = species_uc.charAt( 1 );
482             if ( species_uc.length() > 2 ) {
483                 if ( species_uc.indexOf( " " ) > 0 ) {
484                     third = species_uc.charAt( species_uc.indexOf( " " ) + 1 );
485                 }
486                 else {
487                     third = species_uc.charAt( 2 );
488                 }
489             }
490         }
491         first = AptxUtil.normalizeCharForRGB( first );
492         second = AptxUtil.normalizeCharForRGB( second );
493         third = AptxUtil.normalizeCharForRGB( third );
494         if ( ( first > 235 ) && ( second > 235 ) && ( third > 235 ) ) {
495             first = 0;
496         }
497         else if ( ( first < 80 ) && ( second < 80 ) && ( third < 80 ) ) {
498             second = 255;
499         }
500         return new Color( first, second, third );
501     }
502
503     // Returns true if the specified format name can be written
504     final static boolean canWriteFormat( final String format_name ) {
505         final Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName( format_name );
506         return iter.hasNext();
507     }
508
509     final static void collapseSpeciesSpecificSubtrees( final Phylogeny phy ) {
510         boolean inferred = false;
511         for( final PhylogenyNodeIterator it = phy.iteratorPreorder(); it.hasNext(); ) {
512             final PhylogenyNode n = it.next();
513             if ( !n.isExternal() && !n.isCollapse() && ( n.getNumberOfDescendants() > 1 ) ) {
514                 final Set<Taxonomy> taxs = obtainDistinctTaxonomies( n );
515                 if ( ( taxs != null ) && ( taxs.size() == 1 ) ) {
516                     AptxUtil.collapseSubtree( n, true );
517                     if ( !n.getNodeData().isHasTaxonomy() ) {
518                         n.getNodeData().setTaxonomy( ( Taxonomy ) n.getAllExternalDescendants().get( 0 ).getNodeData()
519                                 .getTaxonomy().copy() );
520                     }
521                     inferred = true;
522                 }
523                 else {
524                     n.setCollapse( false );
525                 }
526             }
527         }
528         if ( inferred ) {
529             phy.setRerootable( false );
530         }
531     }
532
533     final static void collapseSubtree( final PhylogenyNode node, final boolean collapse ) {
534         node.setCollapse( collapse );
535         if ( node.isExternal() ) {
536             return;
537         }
538         final PhylogenyNodeIterator it = new PreorderTreeIterator( node );
539         while ( it.hasNext() ) {
540             it.next().setCollapse( collapse );
541         }
542     }
543
544     final static void colorPhylogenyAccordingToConfidenceValues( final Phylogeny tree, final TreePanel tree_panel ) {
545         double max_conf = 0.0;
546         for( final PhylogenyNodeIterator it = tree.iteratorPreorder(); it.hasNext(); ) {
547             final PhylogenyNode n = it.next();
548             n.getBranchData().setBranchColor( null );
549             if ( n.getBranchData().isHasConfidences() ) {
550                 final double conf = PhylogenyMethods.getConfidenceValue( n );
551                 if ( conf > max_conf ) {
552                     max_conf = conf;
553                 }
554             }
555         }
556         if ( max_conf > 0.0 ) {
557             final Color bg = tree_panel.getTreeColorSet().getBackgroundColor();
558             final Color br = tree_panel.getTreeColorSet().getBranchColor();
559             for( final PhylogenyNodeIterator it = tree.iteratorPreorder(); it.hasNext(); ) {
560                 final PhylogenyNode n = it.next();
561                 if ( n.getBranchData().isHasConfidences() ) {
562                     final double conf = PhylogenyMethods.getConfidenceValue( n );
563                     final BranchColor c = new BranchColor( ForesterUtil.calcColor( conf, 0.0, max_conf, bg, br ) );
564                     colorizeSubtree( n, c );
565                 }
566             }
567         }
568     }
569
570     final static void colorPhylogenyAccordingToExternalTaxonomy( final Phylogeny tree, final TreePanel tree_panel ) {
571         for( final PhylogenyNodeIterator it = tree.iteratorPreorder(); it.hasNext(); ) {
572             it.next().getBranchData().setBranchColor( null );
573         }
574         for( final PhylogenyNodeIterator it = tree.iteratorPreorder(); it.hasNext(); ) {
575             final PhylogenyNode n = it.next();
576             if ( !n.getBranchData().isHasBranchColor() ) {
577                 final Taxonomy tax = PhylogenyMethods.getExternalDescendantsTaxonomy( n );
578                 if ( tax != null ) {
579                     n.getBranchData().setBranchColor( new BranchColor( tree_panel.calculateTaxonomyBasedColor( tax ) ) );
580                     final List<PhylogenyNode> descs = PhylogenyMethods.getAllDescendants( n );
581                     for( final PhylogenyNode desc : descs ) {
582                         desc.getBranchData()
583                                 .setBranchColor( new BranchColor( tree_panel.calculateTaxonomyBasedColor( tax ) ) );
584                     }
585                 }
586             }
587         }
588     }
589
590     final static int colorPhylogenyAccordingToRanks( final Phylogeny tree, final String rank, final TreePanel tree_panel ) {
591         final Map<String, Color> true_lineage_to_color_map = new HashMap<String, Color>();
592         int colorizations = 0;
593         for( final PhylogenyNodeIterator it = tree.iteratorPostorder(); it.hasNext(); ) {
594             final PhylogenyNode n = it.next();
595             if ( n.getNodeData().isHasTaxonomy()
596                     && ( !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() )
597                             || !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getCommonName() ) || !ForesterUtil
598                             .isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) ) {
599                 if ( !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getRank() )
600                         && n.getNodeData().getTaxonomy().getRank().equalsIgnoreCase( rank ) ) {
601                     final BranchColor c = new BranchColor( tree_panel.calculateTaxonomyBasedColor( n.getNodeData()
602                             .getTaxonomy() ) );
603                     colorizeSubtree( n, c );
604                     ++colorizations;
605                     if ( !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
606                         true_lineage_to_color_map.put( n.getNodeData().getTaxonomy().getScientificName(), c.getValue() );
607                     }
608                 }
609             }
610         }
611         for( final PhylogenyNodeIterator it = tree.iteratorPostorder(); it.hasNext(); ) {
612             final PhylogenyNode node = it.next();
613             if ( ( node.getBranchData().getBranchColor() == null ) && node.getNodeData().isHasTaxonomy()
614                     && !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getLineage() ) ) {
615                 boolean success = false;
616                 if ( !true_lineage_to_color_map.isEmpty() ) {
617                     for( final String lin : node.getNodeData().getTaxonomy().getLineage() ) {
618                         if ( true_lineage_to_color_map.containsKey( lin ) ) {
619                             colorizeSubtree( node, new BranchColor( true_lineage_to_color_map.get( lin ) ) );
620                             ++colorizations;
621                             success = true;
622                             break;
623                         }
624                     }
625                 }
626                 if ( !success ) {
627                     final Map<String, String> lineage_to_rank_map = MainPanel.getLineageToRankMap();
628                     for( final String lin : node.getNodeData().getTaxonomy().getLineage() ) {
629                         final Taxonomy temp_tax = new Taxonomy();
630                         temp_tax.setScientificName( lin );
631                         if ( lineage_to_rank_map.containsKey( lin )
632                                 && !ForesterUtil.isEmpty( lineage_to_rank_map.get( lin ) )
633                                 && lineage_to_rank_map.get( lin ).equalsIgnoreCase( rank ) ) {
634                             final BranchColor c = new BranchColor( tree_panel.calculateTaxonomyBasedColor( temp_tax ) );
635                             colorizeSubtree( node, c );
636                             ++colorizations;
637                             true_lineage_to_color_map.put( lin, c.getValue() );
638                             break;
639                         }
640                         else {
641                             UniProtTaxonomy up = null;
642                             try {
643                                 up = TaxonomyDataManager.obtainUniProtTaxonomy( temp_tax, null, null );
644                             }
645                             catch ( final Exception e ) {
646                                 e.printStackTrace();
647                             }
648                             if ( ( up != null ) && !ForesterUtil.isEmpty( up.getRank() ) ) {
649                                 lineage_to_rank_map.put( lin, up.getRank() );
650                                 if ( up.getRank().equalsIgnoreCase( rank ) ) {
651                                     final BranchColor c = new BranchColor( tree_panel.calculateTaxonomyBasedColor( temp_tax ) );
652                                     colorizeSubtree( node, c );
653                                     ++colorizations;
654                                     true_lineage_to_color_map.put( lin, c.getValue() );
655                                     break;
656                                 }
657                             }
658                         }
659                     }
660                 }
661             }
662         }
663         return colorizations;
664     }
665
666     final static String createBasicInformation( final Phylogeny phy ) {
667         final StringBuilder desc = new StringBuilder();
668         if ( ( phy != null ) && !phy.isEmpty() ) {
669             if ( !ForesterUtil.isEmpty( phy.getName() ) ) {
670                 desc.append( "Name: " );
671                 desc.append( phy.getName() );
672                 desc.append( "\n" );
673             }
674             if ( phy.getIdentifier() != null ) {
675                 desc.append( "Id: " );
676                 desc.append( phy.getIdentifier().toString() );
677                 desc.append( "\n" );
678             }
679             if ( !ForesterUtil.isEmpty( phy.getDescription() ) ) {
680                 desc.append( "Description: " );
681                 desc.append( phy.getDescription() );
682                 desc.append( "\n" );
683             }
684             if ( !ForesterUtil.isEmpty( phy.getDistanceUnit() ) ) {
685                 desc.append( "Distance Unit: " );
686                 desc.append( phy.getDistanceUnit() );
687                 desc.append( "\n" );
688             }
689             if ( !ForesterUtil.isEmpty( phy.getType() ) ) {
690                 desc.append( "Type: " );
691                 desc.append( phy.getType() );
692                 desc.append( "\n" );
693             }
694             desc.append( "Rooted: " );
695             desc.append( phy.isRooted() );
696             desc.append( "\n" );
697             desc.append( "Rerootable: " );
698             desc.append( phy.isRerootable() );
699             desc.append( "\n" );
700             desc.append( "Nodes: " );
701             desc.append( phy.getNodeCount() );
702             desc.append( "\n" );
703             desc.append( "External nodes: " );
704             desc.append( phy.getNumberOfExternalNodes() );
705             desc.append( "\n" );
706             desc.append( "Internal nodes: " );
707             desc.append( phy.getNodeCount() - phy.getNumberOfExternalNodes() );
708             desc.append( "\n" );
709             desc.append( "Internal nodes with polytomies: " );
710             desc.append( PhylogenyMethods.countNumberOfPolytomies( phy ) );
711             desc.append( "\n" );
712             desc.append( "Branches: " );
713             desc.append( phy.getNumberOfBranches() );
714             desc.append( "\n" );
715             desc.append( "Depth: " );
716             desc.append( PhylogenyMethods.calculateMaxDepth( phy ) );
717             desc.append( "\n" );
718             desc.append( "Maximum distance to root: " );
719             desc.append( ForesterUtil.round( PhylogenyMethods.calculateMaxDistanceToRoot( phy ), 6 ) );
720             desc.append( "\n" );
721             final Set<Taxonomy> taxs = obtainAllDistinctTaxonomies( phy.getRoot() );
722             if ( taxs != null ) {
723                 desc.append( "Distinct external taxonomies: " );
724                 desc.append( taxs.size() );
725             }
726             desc.append( "\n" );
727             final DescriptiveStatistics bs = PhylogenyMethods.calculatBranchLengthStatistics( phy );
728             if ( bs.getN() > 3 ) {
729                 desc.append( "\n" );
730                 desc.append( "Branch-length statistics: " );
731                 desc.append( "\n" );
732                 desc.append( "    Number of branches with non-negative branch-lengths: " + bs.getN() );
733                 desc.append( "\n" );
734                 desc.append( "    Median: " + ForesterUtil.round( bs.median(), 6 ) );
735                 desc.append( "\n" );
736                 desc.append( "    Mean: " + ForesterUtil.round( bs.arithmeticMean(), 6 ) );
737                 desc.append( "\n" );
738                 desc.append( "    SD: " + ForesterUtil.round( bs.sampleStandardDeviation(), 6 ) );
739                 desc.append( "\n" );
740                 desc.append( "    Minimum: " + ForesterUtil.round( bs.getMin(), 6 ) );
741                 desc.append( "\n" );
742                 desc.append( "    Maximum: " + ForesterUtil.round( bs.getMax(), 6 ) );
743                 desc.append( "\n" );
744                 if ( Math.abs( bs.getMax() - bs.getMin() ) > 0.0001 ) {
745                     desc.append( "\n" );
746                     final AsciiHistogram histo = new AsciiHistogram( bs );
747                     desc.append( histo.toStringBuffer( 12, '#', 40, 7, "    " ) );
748                 }
749             }
750             final DescriptiveStatistics ds = PhylogenyMethods.calculatNumberOfDescendantsPerNodeStatistics( phy );
751             if ( ds.getN() > 2 ) {
752                 desc.append( "\n" );
753                 desc.append( "Descendants per node statistics: " );
754                 desc.append( "\n" );
755                 desc.append( "    Median: " + ForesterUtil.round( ds.median(), 2 ) );
756                 desc.append( "\n" );
757                 desc.append( "    Mean: " + ForesterUtil.round( ds.arithmeticMean(), 2 ) );
758                 desc.append( "\n" );
759                 desc.append( "    SD: " + ForesterUtil.round( ds.sampleStandardDeviation(), 2 ) );
760                 desc.append( "\n" );
761                 desc.append( "    Minimum: " + ForesterUtil.roundToInt( ds.getMin() ) );
762                 desc.append( "\n" );
763                 desc.append( "    Maximum: " + ForesterUtil.roundToInt( ds.getMax() ) );
764                 desc.append( "\n" );
765             }
766             List<DescriptiveStatistics> css = null;
767             try {
768                 css = PhylogenyMethods.calculatConfidenceStatistics( phy );
769             }
770             catch ( final IllegalArgumentException e ) {
771                 ForesterUtil.printWarningMessage( Constants.PRG_NAME, e.getMessage() );
772             }
773             if ( ( css != null ) && ( css.size() > 0 ) ) {
774                 desc.append( "\n" );
775                 for( int i = 0; i < css.size(); ++i ) {
776                     final DescriptiveStatistics cs = css.get( i );
777                     if ( ( cs != null ) && ( cs.getN() > 1 ) ) {
778                         if ( css.size() > 1 ) {
779                             desc.append( "Support statistics " + ( i + 1 ) + ": " );
780                         }
781                         else {
782                             desc.append( "Support statistics: " );
783                         }
784                         if ( !ForesterUtil.isEmpty( cs.getDescription() ) ) {
785                             desc.append( "\n" );
786                             desc.append( "    Type: " + cs.getDescription() );
787                         }
788                         desc.append( "\n" );
789                         desc.append( "    Branches with support: " + cs.getN() );
790                         desc.append( "\n" );
791                         desc.append( "    Median: " + ForesterUtil.round( cs.median(), 6 ) );
792                         desc.append( "\n" );
793                         desc.append( "    Mean: " + ForesterUtil.round( cs.arithmeticMean(), 6 ) );
794                         desc.append( "\n" );
795                         if ( cs.getN() > 2 ) {
796                             desc.append( "    SD: " + ForesterUtil.round( cs.sampleStandardDeviation(), 6 ) );
797                             desc.append( "\n" );
798                         }
799                         desc.append( "    Minimum: " + ForesterUtil.roundToInt( cs.getMin() ) );
800                         desc.append( "\n" );
801                         desc.append( "    Maximum: " + ForesterUtil.roundToInt( cs.getMax() ) );
802                         desc.append( "\n" );
803                     }
804                 }
805             }
806         }
807         return desc.toString();
808     }
809
810     /**
811      * Exits with -1.
812      * 
813      * 
814      * @param message
815      *            to message to be printed
816      */
817     final static void dieWithSystemError( final String message ) {
818         System.out.println();
819         System.out.println( Constants.PRG_NAME + " encountered the following system error: " + message );
820         System.out.println( "Please contact the authors." );
821         System.out.println( Constants.PRG_NAME + " needs to close." );
822         System.out.println();
823         System.exit( -1 );
824     }
825
826     final static String[] getAllPossibleRanks() {
827         final String[] str_array = new String[ PhyloXmlUtil.TAXONOMY_RANKS_LIST.size() - 2 ];
828         int i = 0;
829         for( final String e : PhyloXmlUtil.TAXONOMY_RANKS_LIST ) {
830             if ( !e.equals( PhyloXmlUtil.UNKNOWN ) && !e.equals( PhyloXmlUtil.OTHER ) ) {
831                 str_array[ i++ ] = e;
832             }
833         }
834         return str_array;
835     }
836
837     final static String[] getAllRanks( final Phylogeny tree ) {
838         final SortedSet<String> ranks = new TreeSet<String>();
839         for( final PhylogenyNodeIterator it = tree.iteratorPreorder(); it.hasNext(); ) {
840             final PhylogenyNode n = it.next();
841             if ( n.getNodeData().isHasTaxonomy() && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getRank() ) ) {
842                 ranks.add( n.getNodeData().getTaxonomy().getRank() );
843             }
844         }
845         return ForesterUtil.stringSetToArray( ranks );
846     }
847
848     final static String[] getAvailableFontFamiliesSorted() {
849         return AVAILABLE_FONT_FAMILIES_SORTED;
850     }
851
852     final static boolean isHasAssignedEvent( final PhylogenyNode node ) {
853         if ( !node.getNodeData().isHasEvent() ) {
854             return false;
855         }
856         if ( ( node.getNodeData().getEvent() ).isUnassigned() ) {
857             return false;
858         }
859         return true;
860     }
861
862     final static boolean isMac() {
863         try {
864             final String s = ForesterUtil.OS_NAME.toLowerCase();
865             return s.startsWith( "mac" );
866         }
867         catch ( final Exception e ) {
868             ForesterUtil.printWarningMessage( Constants.PRG_NAME, "minor error: " + e );
869             return false;
870         }
871     }
872
873     final static boolean isUsOrCanada() {
874         try {
875             if ( ( Locale.getDefault().equals( Locale.CANADA ) ) || ( Locale.getDefault().equals( Locale.US ) ) ) {
876                 return true;
877             }
878         }
879         catch ( final Exception e ) {
880             return false;
881         }
882         return false;
883     }
884
885     final static boolean isWindows() {
886         try {
887             final String s = ForesterUtil.OS_NAME.toLowerCase();
888             return s.indexOf( "win" ) > -1;
889         }
890         catch ( final Exception e ) {
891             ForesterUtil.printWarningMessage( Constants.PRG_NAME, "minor error: " + e );
892             return false;
893         }
894     }
895
896     final static void lookAtSomeTreePropertiesForAptxControlSettings( final Phylogeny t,
897                                                                       final ControlPanel atv_control,
898                                                                       final Configuration configuration ) {
899         if ( ( t != null ) && !t.isEmpty() ) {
900             if ( !AptxUtil.isHasAtLeastOneBranchLengthLargerThanZero( t ) ) {
901                 atv_control.setDrawPhylogram( false );
902                 atv_control.setDrawPhylogramEnabled( false );
903             }
904             if ( configuration.doGuessCheckOption( Configuration.display_as_phylogram ) ) {
905                 if ( atv_control.getDisplayAsPhylogramCb() != null ) {
906                     if ( AptxUtil.isHasAtLeastOneBranchLengthLargerThanZero( t ) ) {
907                         atv_control.setDrawPhylogram( true );
908                         atv_control.setDrawPhylogramEnabled( true );
909                     }
910                     else {
911                         atv_control.setDrawPhylogram( false );
912                     }
913                 }
914             }
915             if ( configuration.doGuessCheckOption( Configuration.write_confidence_values ) ) {
916                 if ( atv_control.getWriteConfidenceCb() != null ) {
917                     if ( AptxUtil.isHasAtLeastOneBranchWithSupportValues( t ) ) {
918                         atv_control.setCheckbox( Configuration.write_confidence_values, true );
919                     }
920                     else {
921                         atv_control.setCheckbox( Configuration.write_confidence_values, false );
922                     }
923                 }
924             }
925             if ( configuration.doGuessCheckOption( Configuration.write_events ) ) {
926                 if ( atv_control.getShowEventsCb() != null ) {
927                     if ( AptxUtil.isHasAtLeastNodeWithEvent( t ) ) {
928                         atv_control.setCheckbox( Configuration.write_events, true );
929                     }
930                     else {
931                         atv_control.setCheckbox( Configuration.write_events, false );
932                     }
933                 }
934             }
935         }
936     }
937
938     final static void openWebsite( final String url, final boolean is_applet, final JApplet applet ) throws IOException {
939         try {
940             AptxUtil.launchWebBrowser( new URI( url ), is_applet, applet, Constants.PRG_NAME );
941         }
942         catch ( final Exception e ) {
943             throw new IOException( e );
944         }
945     }
946
947     final static void outOfMemoryError( final OutOfMemoryError e ) {
948         System.err.println();
949         System.err.println( "Java memory allocation might be too small, try \"-Xmx2048m\" java command line option" );
950         System.err.println();
951         e.printStackTrace();
952         System.err.println();
953         JOptionPane.showMessageDialog( null,
954                                        "Java memory allocation might be too small, try \"-Xmx2048m\" java command line option"
955                                                + "\n\nError: " + e.getLocalizedMessage(),
956                                        "Out of Memory Error [" + Constants.PRG_NAME + " " + Constants.VERSION + "]",
957                                        JOptionPane.ERROR_MESSAGE );
958         System.exit( -1 );
959     }
960
961     final static void printAppletMessage( final String applet_name, final String message ) {
962         System.out.println( "[" + applet_name + "] > " + message );
963     }
964
965     final static Phylogeny[] readPhylogeniesFromUrl( final URL url,
966                                                      final boolean phyloxml_validate_against_xsd,
967                                                      final boolean replace_underscores,
968                                                      final boolean internal_numbers_are_confidences,
969                                                      final TAXONOMY_EXTRACTION taxonomy_extraction )
970             throws FileNotFoundException, IOException {
971         final PhylogenyFactory factory = ParserBasedPhylogenyFactory.getInstance();
972         final PhylogenyParser parser;
973         boolean nhx_or_nexus = false;
974         if ( url.getHost().toLowerCase().indexOf( "tolweb" ) >= 0 ) {
975             parser = new TolParser();
976         }
977         else {
978             parser = ParserUtils.createParserDependingOnUrlContents( url, phyloxml_validate_against_xsd );
979             if ( parser instanceof NHXParser ) {
980                 nhx_or_nexus = true;
981                 final NHXParser nhx = ( NHXParser ) parser;
982                 nhx.setReplaceUnderscores( replace_underscores );
983                 nhx.setIgnoreQuotes( false );
984                 nhx.setTaxonomyExtraction( taxonomy_extraction );
985             }
986             else if ( parser instanceof NexusPhylogeniesParser ) {
987                 nhx_or_nexus = true;
988                 final NexusPhylogeniesParser nex = ( NexusPhylogeniesParser ) parser;
989                 nex.setReplaceUnderscores( replace_underscores );
990                 nex.setIgnoreQuotes( false );
991             }
992         }
993         final Phylogeny[] phys = factory.create( url.openStream(), parser );
994         if ( nhx_or_nexus && internal_numbers_are_confidences ) {
995             for( final Phylogeny phy : phys ) {
996                 PhylogenyMethods.transferInternalNodeNamesToConfidence( phy );
997             }
998         }
999         return phys;
1000     }
1001
1002     final static void removeBranchColors( final Phylogeny phy ) {
1003         for( final PhylogenyNodeIterator it = phy.iteratorPreorder(); it.hasNext(); ) {
1004             it.next().getBranchData().setBranchColor( null );
1005         }
1006     }
1007
1008     final static void unexpectedError( final Error e ) {
1009         System.err.println();
1010         e.printStackTrace( System.err );
1011         System.err.println();
1012         final StringBuffer sb = new StringBuffer();
1013         for( final StackTraceElement s : e.getStackTrace() ) {
1014             sb.append( s + "\n" );
1015         }
1016         JOptionPane
1017                 .showMessageDialog( null,
1018                                     "An unexpected (possibly severe) error has occured - terminating. \nPlease contact: "
1019                                             + Constants.AUTHOR_EMAIL + " \nError: " + e.getLocalizedMessage() + "\n"
1020                                             + sb,
1021                                     "Unexpected Severe Error [" + Constants.PRG_NAME + " " + Constants.VERSION + "]",
1022                                     JOptionPane.ERROR_MESSAGE );
1023         System.exit( -1 );
1024     }
1025
1026     final static void unexpectedException( final Exception e ) {
1027         System.err.println();
1028         e.printStackTrace( System.err );
1029         System.err.println();
1030         final StringBuffer sb = new StringBuffer();
1031         for( final StackTraceElement s : e.getStackTrace() ) {
1032             sb.append( s + "\n" );
1033         }
1034         JOptionPane.showMessageDialog( null,
1035                                        "An unexpected exception has occured. \nPlease contact: "
1036                                                + Constants.AUTHOR_EMAIL + " \nException: " + e.getLocalizedMessage()
1037                                                + "\n" + sb,
1038                                        "Unexpected Exception [" + Constants.PRG_NAME + Constants.VERSION + "]",
1039                                        JOptionPane.ERROR_MESSAGE );
1040     }
1041
1042     final static String writePhylogenyToGraphicsByteArrayOutputStream( final ByteArrayOutputStream baos,
1043                                                                        int width,
1044                                                                        int height,
1045                                                                        final TreePanel tree_panel,
1046                                                                        final ControlPanel ac,
1047                                                                        final GraphicsExportType type,
1048                                                                        final Options options ) throws IOException {
1049         if ( !options.isGraphicsExportUsingActualSize() ) {
1050             if ( options.isGraphicsExportVisibleOnly() ) {
1051                 throw new IllegalArgumentException( "cannot export visible rectangle only without exporting in actual size" );
1052             }
1053             tree_panel.calcParametersForPainting( options.getPrintSizeX(), options.getPrintSizeY(), true );
1054             tree_panel.resetPreferredSize();
1055             tree_panel.repaint();
1056         }
1057         final RenderingHints rendering_hints = new RenderingHints( RenderingHints.KEY_RENDERING,
1058                                                                    RenderingHints.VALUE_RENDER_QUALITY );
1059         rendering_hints.put( RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY );
1060         if ( options.isAntialiasPrint() ) {
1061             rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
1062             rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
1063         }
1064         else {
1065             rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF );
1066             rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
1067         }
1068         final Phylogeny phylogeny = tree_panel.getPhylogeny();
1069         if ( ( phylogeny == null ) || phylogeny.isEmpty() ) {
1070             return "";
1071         }
1072         Rectangle visible = null;
1073         if ( !options.isGraphicsExportUsingActualSize() ) {
1074             width = options.getPrintSizeX();
1075             height = options.getPrintSizeY();
1076         }
1077         else if ( options.isGraphicsExportVisibleOnly() ) {
1078             visible = tree_panel.getVisibleRect();
1079             width = visible.width;
1080             height = visible.height;
1081         }
1082         final BufferedImage buffered_img = new BufferedImage( width, height, BufferedImage.TYPE_INT_RGB );
1083         Graphics2D g2d = buffered_img.createGraphics();
1084         g2d.setRenderingHints( rendering_hints );
1085         int x = 0;
1086         int y = 0;
1087         if ( options.isGraphicsExportVisibleOnly() ) {
1088             g2d = ( Graphics2D ) g2d.create( -visible.x, -visible.y, visible.width, visible.height );
1089             g2d.setClip( null );
1090             x = visible.x;
1091             y = visible.y;
1092         }
1093         tree_panel.paintPhylogeny( g2d, false, true, width, height, x, y );
1094         ImageIO.write( buffered_img, type.toString(), baos );
1095         g2d.dispose();
1096         System.gc();
1097         if ( !options.isGraphicsExportUsingActualSize() ) {
1098             tree_panel.getMainPanel().getControlPanel().showWhole();
1099         }
1100         String msg = baos.toString();
1101         if ( ( width > 0 ) && ( height > 0 ) ) {
1102             msg += " [size: " + width + ", " + height + "]";
1103         }
1104         return msg;
1105     }
1106
1107     final static String writePhylogenyToGraphicsFile( final String file_name,
1108                                                       int width,
1109                                                       int height,
1110                                                       final TreePanel tree_panel,
1111                                                       final ControlPanel ac,
1112                                                       final GraphicsExportType type,
1113                                                       final Options options ) throws IOException {
1114         if ( !options.isGraphicsExportUsingActualSize() ) {
1115             if ( options.isGraphicsExportVisibleOnly() ) {
1116                 throw new IllegalArgumentException( "cannot export visible rectangle only without exporting in actual size" );
1117             }
1118             tree_panel.calcParametersForPainting( options.getPrintSizeX(), options.getPrintSizeY(), true );
1119             tree_panel.resetPreferredSize();
1120             tree_panel.repaint();
1121         }
1122         final RenderingHints rendering_hints = new RenderingHints( RenderingHints.KEY_RENDERING,
1123                                                                    RenderingHints.VALUE_RENDER_QUALITY );
1124         rendering_hints.put( RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY );
1125         if ( options.isAntialiasPrint() ) {
1126             rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
1127             rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
1128         }
1129         else {
1130             rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF );
1131             rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
1132         }
1133         final Phylogeny phylogeny = tree_panel.getPhylogeny();
1134         if ( ( phylogeny == null ) || phylogeny.isEmpty() ) {
1135             return "";
1136         }
1137         final File file = new File( file_name );
1138         if ( file.isDirectory() ) {
1139             throw new IOException( "\"" + file_name + "\" is a directory" );
1140         }
1141         Rectangle visible = null;
1142         if ( !options.isGraphicsExportUsingActualSize() ) {
1143             width = options.getPrintSizeX();
1144             height = options.getPrintSizeY();
1145         }
1146         else if ( options.isGraphicsExportVisibleOnly() ) {
1147             visible = tree_panel.getVisibleRect();
1148             width = visible.width;
1149             height = visible.height;
1150         }
1151         final BufferedImage buffered_img = new BufferedImage( width, height, BufferedImage.TYPE_INT_RGB );
1152         Graphics2D g2d = buffered_img.createGraphics();
1153         g2d.setRenderingHints( rendering_hints );
1154         int x = 0;
1155         int y = 0;
1156         if ( options.isGraphicsExportVisibleOnly() ) {
1157             g2d = ( Graphics2D ) g2d.create( -visible.x, -visible.y, visible.width, visible.height );
1158             g2d.setClip( null );
1159             x = visible.x;
1160             y = visible.y;
1161         }
1162         tree_panel.paintPhylogeny( g2d, false, true, width, height, x, y );
1163         if ( type == GraphicsExportType.TIFF ) {
1164             writeToTiff( file, buffered_img );
1165         }
1166         else {
1167             ImageIO.write( buffered_img, type.toString(), file );
1168         }
1169         g2d.dispose();
1170         System.gc();
1171         if ( !options.isGraphicsExportUsingActualSize() ) {
1172             tree_panel.getMainPanel().getControlPanel().showWhole();
1173         }
1174         String msg = file.toString();
1175         if ( ( width > 0 ) && ( height > 0 ) ) {
1176             msg += " [size: " + width + ", " + height + "]";
1177         }
1178         return msg;
1179     }
1180
1181     final static void writeToTiff( final File file, final BufferedImage image ) throws IOException {
1182         // See: http://log.robmeek.com/2005/08/write-tiff-in-java.html
1183         ImageWriter writer = null;
1184         ImageOutputStream ios = null;
1185         // Find an appropriate writer:
1186         final Iterator<ImageWriter> it = ImageIO.getImageWritersByFormatName( "TIF" );
1187         if ( it.hasNext() ) {
1188             writer = it.next();
1189         }
1190         else {
1191             throw new IOException( "failed to get TIFF image writer" );
1192         }
1193         // Setup writer:
1194         ios = ImageIO.createImageOutputStream( file );
1195         writer.setOutput( ios );
1196         final ImageWriteParam image_write_param = new ImageWriteParam( Locale.getDefault() );
1197         image_write_param.setCompressionMode( ImageWriteParam.MODE_EXPLICIT );
1198         // see writeParam.getCompressionTypes() for available compression type
1199         // strings.
1200         image_write_param.setCompressionType( "PackBits" );
1201         final String t[] = image_write_param.getCompressionTypes();
1202         for( final String string : t ) {
1203             System.out.println( string );
1204         }
1205         // Convert to an IIOImage:
1206         final IIOImage iio_image = new IIOImage( image, null, null );
1207         writer.write( null, iio_image, image_write_param );
1208     }
1209
1210     private static void colorizeSubtree( final PhylogenyNode node, final BranchColor c ) {
1211         node.getBranchData().setBranchColor( c );
1212         final List<PhylogenyNode> descs = PhylogenyMethods.getAllDescendants( node );
1213         for( final PhylogenyNode desc : descs ) {
1214             desc.getBranchData().setBranchColor( c );
1215         }
1216     }
1217
1218     final private static char normalizeCharForRGB( char c ) {
1219         c -= 65;
1220         c *= 10.2;
1221         c = c > 255 ? 255 : c;
1222         c = c < 0 ? 0 : c;
1223         return c;
1224     }
1225
1226     final private static void openUrlInWebBrowser( final String url ) throws IOException, ClassNotFoundException,
1227             SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException,
1228             InvocationTargetException, InterruptedException {
1229         final String os = System.getProperty( "os.name" );
1230         final Runtime runtime = Runtime.getRuntime();
1231         if ( os.toLowerCase().startsWith( "win" ) ) {
1232             Runtime.getRuntime().exec( "rundll32 url.dll,FileProtocolHandler " + url );
1233         }
1234         else if ( isMac() ) {
1235             final Class<?> file_mgr = Class.forName( "com.apple.eio.FileManager" );
1236             final Method open_url = file_mgr.getDeclaredMethod( "openURL", new Class[] { String.class } );
1237             open_url.invoke( null, new Object[] { url } );
1238         }
1239         else {
1240             final String[] browsers = { "firefox", "opera", "konqueror", "mozilla", "netscape", "epiphany" };
1241             String browser = null;
1242             for( int i = 0; ( i < browsers.length ) && ( browser == null ); ++i ) {
1243                 if ( runtime.exec( new String[] { "which", browsers[ i ] } ).waitFor() == 0 ) {
1244                     browser = browsers[ i ];
1245                 }
1246             }
1247             if ( browser == null ) {
1248                 throw new IOException( "could not find a web browser to open [" + url + "] in" );
1249             }
1250             else {
1251                 runtime.exec( new String[] { browser, url } );
1252             }
1253         }
1254     }
1255
1256     // See: http://www.xml.nig.ac.jp/tutorial/rest/index.html#2.2
1257     // static void openDDBJRest() throws IOException {
1258     // //set URL
1259     // URL url = new URL( "http://xml.nig.ac.jp/rest/Invoke" );
1260     // //set parameter
1261     // String query = "service=GetEntry&method=getDDBJEntry&accession=AB000100";
1262     // //make connection
1263     // URLConnection urlc = url.openConnection();
1264     // //use post mode
1265     // urlc.setDoOutput( true );
1266     // urlc.setAllowUserInteraction( false );
1267     // //send query
1268     // PrintStream ps = new PrintStream( urlc.getOutputStream() );
1269     // ps.print( query );
1270     // ps.close();
1271     // //get result
1272     // BufferedReader br = new BufferedReader( new InputStreamReader(
1273     // urlc.getInputStream() ) );
1274     // String l = null;
1275     // while ( ( l = br.readLine() ) != null ) {
1276     // System.out.println( l );
1277     // }
1278     // br.close();
1279     // }
1280     public static enum GraphicsExportType {
1281         GIF( "gif" ), JPG( "jpg" ), PDF( "pdf" ), PNG( "png" ), TIFF( "tif" ), BMP( "bmp" );
1282
1283         private final String _suffix;
1284
1285         private GraphicsExportType( final String suffix ) {
1286             _suffix = suffix;
1287         }
1288
1289         @Override
1290         public String toString() {
1291             return _suffix;
1292         }
1293     }
1294 }