in progress
[jalview.git] / forester / java / src / org / forester / util / ForesterUtil.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: www.phylosoft.org/forester
25
26 package org.forester.util;
27
28 import java.awt.Color;
29 import java.io.BufferedReader;
30 import java.io.BufferedWriter;
31 import java.io.File;
32 import java.io.FileInputStream;
33 import java.io.FileNotFoundException;
34 import java.io.FileOutputStream;
35 import java.io.FileReader;
36 import java.io.FileWriter;
37 import java.io.IOException;
38 import java.io.InputStream;
39 import java.io.InputStreamReader;
40 import java.io.StringReader;
41 import java.math.BigDecimal;
42 import java.net.URL;
43 import java.text.DateFormat;
44 import java.text.DecimalFormat;
45 import java.text.DecimalFormatSymbols;
46 import java.text.NumberFormat;
47 import java.text.ParseException;
48 import java.text.SimpleDateFormat;
49 import java.util.ArrayList;
50 import java.util.Date;
51 import java.util.Hashtable;
52 import java.util.Iterator;
53 import java.util.List;
54 import java.util.Map;
55 import java.util.Set;
56 import java.util.SortedMap;
57 import java.util.SortedSet;
58 import java.util.TreeMap;
59 import java.util.TreeSet;
60 import java.util.regex.Matcher;
61 import java.util.regex.Pattern;
62
63 import org.forester.io.parsers.PhylogenyParser;
64 import org.forester.io.parsers.nexus.NexusPhylogeniesParser;
65 import org.forester.io.parsers.nhx.NHXParser;
66 import org.forester.io.parsers.phyloxml.PhyloXmlParser;
67 import org.forester.io.parsers.phyloxml.PhyloXmlUtil;
68 import org.forester.io.parsers.tol.TolParser;
69 import org.forester.io.parsers.util.PhylogenyParserException;
70 import org.forester.phylogeny.Phylogeny;
71 import org.forester.phylogeny.PhylogenyMethods;
72 import org.forester.phylogeny.PhylogenyNode;
73 import org.forester.phylogeny.data.Confidence;
74 import org.forester.phylogeny.data.Distribution;
75 import org.forester.phylogeny.data.Identifier;
76 import org.forester.phylogeny.data.Sequence;
77 import org.forester.phylogeny.data.Taxonomy;
78 import org.forester.phylogeny.factories.ParserBasedPhylogenyFactory;
79 import org.forester.phylogeny.factories.PhylogenyFactory;
80 import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
81
82 public final class ForesterUtil {
83
84     public final static String       FILE_SEPARATOR                   = System.getProperty( "file.separator" );
85     public final static String       LINE_SEPARATOR                   = System.getProperty( "line.separator" );
86     public final static String       JAVA_VENDOR                      = System.getProperty( "java.vendor" );
87     public final static String       JAVA_VERSION                     = System.getProperty( "java.version" );
88     public final static String       OS_ARCH                          = System.getProperty( "os.arch" );
89     public final static String       OS_NAME                          = System.getProperty( "os.name" );
90     public final static String       OS_VERSION                       = System.getProperty( "os.version" );
91     public final static Pattern      PARANTHESESABLE_NH_CHARS_PATTERN = Pattern.compile( "[(),;\\s]" );
92     public final static double       ZERO_DIFF                        = 1.0E-9;
93     public static final BigDecimal   NULL_BD                          = new BigDecimal( 0 );
94     public static final NumberFormat FORMATTER_9;
95     public static final NumberFormat FORMATTER_6;
96     public static final NumberFormat FORMATTER_06;
97     public static final NumberFormat FORMATTER_3;
98     static {
99         final DecimalFormatSymbols dfs = new DecimalFormatSymbols();
100         dfs.setDecimalSeparator( '.' );
101         // dfs.setGroupingSeparator( ( char ) 0 );
102         FORMATTER_9 = new DecimalFormat( "#.#########", dfs );
103         FORMATTER_6 = new DecimalFormat( "#.######", dfs );
104         FORMATTER_06 = new DecimalFormat( "0.######", dfs );
105         FORMATTER_3 = new DecimalFormat( "#.###", dfs );
106     }
107
108     private ForesterUtil() {
109     }
110
111     public final static Phylogeny[] readPhylogenies( final PhylogenyParser parser, final File file ) throws IOException {
112         final PhylogenyFactory factory = ParserBasedPhylogenyFactory.getInstance();
113         final Phylogeny[] trees = factory.create( file, parser );
114         if ( ( trees == null ) || ( trees.length == 0 ) ) {
115             throw new PhylogenyParserException( "Unable to parse phylogeny from file: " + file );
116         }
117         return trees;
118     }
119
120     final public static void appendSeparatorIfNotEmpty( final StringBuffer sb, final char separator ) {
121         if ( sb.length() > 0 ) {
122             sb.append( separator );
123         }
124     }
125
126     final public static boolean isEmpty( final List<?> l ) {
127         if ( ( l == null ) || l.isEmpty() ) {
128             return true;
129         }
130         for( final Object o : l ) {
131             if ( o != null ) {
132                 return false;
133             }
134         }
135         return true;
136     }
137
138     final public static boolean isEmpty( final Set<?> s ) {
139         if ( ( s == null ) || s.isEmpty() ) {
140             return true;
141         }
142         for( final Object o : s ) {
143             if ( o != null ) {
144                 return false;
145             }
146         }
147         return true;
148     }
149
150     /**
151      * This calculates a color. If value is equal to min the returned color is
152      * minColor, if value is equal to max the returned color is maxColor,
153      * otherwise a color 'proportional' to value is returned.
154      * 
155      * @param value
156      *            the value 
157      * @param min
158      *            the smallest value 
159      * @param max
160      *            the largest value 
161      * @param minColor
162      *            the color for min
163      * @param maxColor
164      *            the color for max
165      * @return a Color
166      */
167     final public static Color calcColor( double value,
168                                          final double min,
169                                          final double max,
170                                          final Color minColor,
171                                          final Color maxColor ) {
172         if ( value < min ) {
173             value = min;
174         }
175         if ( value > max ) {
176             value = max;
177         }
178         final double x = ForesterUtil.calculateColorFactor( value, max, min );
179         final int red = ForesterUtil.calculateColorComponent( minColor.getRed(), maxColor.getRed(), x );
180         final int green = ForesterUtil.calculateColorComponent( minColor.getGreen(), maxColor.getGreen(), x );
181         final int blue = ForesterUtil.calculateColorComponent( minColor.getBlue(), maxColor.getBlue(), x );
182         return new Color( red, green, blue );
183     }
184
185     /**
186      * This calculates a color. If value is equal to min the returned color is
187      * minColor, if value is equal to max the returned color is maxColor, if
188      * value is equal to mean the returned color is meanColor, otherwise a color
189      * 'proportional' to value is returned -- either between min-mean or
190      * mean-max
191      * 
192      * @param value
193      *            the value
194      * @param min
195      *            the smallest value
196      * @param max
197      *            the largest value 
198      * @param mean
199      *            the mean/median value 
200      * @param minColor
201      *            the color for min
202      * @param maxColor
203      *            the color for max
204      * @param meanColor
205      *            the color for mean
206      * @return a Color
207      */
208     final public static Color calcColor( double value,
209                                          final double min,
210                                          final double max,
211                                          final double mean,
212                                          final Color minColor,
213                                          final Color maxColor,
214                                          final Color meanColor ) {
215         if ( value < min ) {
216             value = min;
217         }
218         if ( value > max ) {
219             value = max;
220         }
221         if ( value < mean ) {
222             final double x = ForesterUtil.calculateColorFactor( value, mean, min );
223             final int red = ForesterUtil.calculateColorComponent( minColor.getRed(), meanColor.getRed(), x );
224             final int green = ForesterUtil.calculateColorComponent( minColor.getGreen(), meanColor.getGreen(), x );
225             final int blue = ForesterUtil.calculateColorComponent( minColor.getBlue(), meanColor.getBlue(), x );
226             return new Color( red, green, blue );
227         }
228         else if ( value > mean ) {
229             final double x = ForesterUtil.calculateColorFactor( value, max, mean );
230             final int red = ForesterUtil.calculateColorComponent( meanColor.getRed(), maxColor.getRed(), x );
231             final int green = ForesterUtil.calculateColorComponent( meanColor.getGreen(), maxColor.getGreen(), x );
232             final int blue = ForesterUtil.calculateColorComponent( meanColor.getBlue(), maxColor.getBlue(), x );
233             return new Color( red, green, blue );
234         }
235         else {
236             return meanColor;
237         }
238     }
239
240     /**
241      * Helper method for calcColor methods.
242      * 
243      * @param smallercolor_component_x
244      *            color component the smaller color
245      * @param largercolor_component_x
246      *            color component the larger color
247      * @param x
248      *            factor
249      * @return an int representing a color component
250      */
251     final private static int calculateColorComponent( final double smallercolor_component_x,
252                                                       final double largercolor_component_x,
253                                                       final double x ) {
254         return ( int ) ( smallercolor_component_x + ( ( x * ( largercolor_component_x - smallercolor_component_x ) ) / 255.0 ) );
255     }
256
257     /**
258      * Helper method for calcColor methods.
259      * 
260      * 
261      * @param value
262      *            the value
263      * @param larger
264      *            the largest value
265      * @param smaller
266      *            the smallest value
267      * @return a normalized value between larger and smaller
268      */
269     final private static double calculateColorFactor( final double value, final double larger, final double smaller ) {
270         return ( 255.0 * ( value - smaller ) ) / ( larger - smaller );
271     }
272
273     final public static String collapseWhiteSpace( final String s ) {
274         return s.replaceAll( "[\\s]+", " " );
275     }
276
277     final public static String colorToHex( final Color color ) {
278         final String rgb = Integer.toHexString( color.getRGB() );
279         return rgb.substring( 2, rgb.length() );
280     }
281
282     synchronized public static void copyFile( final File in, final File out ) throws IOException {
283         final FileInputStream in_s = new FileInputStream( in );
284         final FileOutputStream out_s = new FileOutputStream( out );
285         try {
286             final byte[] buf = new byte[ 1024 ];
287             int i = 0;
288             while ( ( i = in_s.read( buf ) ) != -1 ) {
289                 out_s.write( buf, 0, i );
290             }
291         }
292         catch ( final IOException e ) {
293             throw e;
294         }
295         finally {
296             if ( in_s != null ) {
297                 in_s.close();
298             }
299             if ( out_s != null ) {
300                 out_s.close();
301             }
302         }
303     }
304
305     final public static int countChars( final String str, final char c ) {
306         int count = 0;
307         for( int i = 0; i < str.length(); ++i ) {
308             if ( str.charAt( i ) == c ) {
309                 ++count;
310             }
311         }
312         return count;
313     }
314
315     final public static BufferedWriter createBufferedWriter( final File file ) throws IOException {
316         if ( file.exists() ) {
317             throw new IOException( "[" + file + "] already exists" );
318         }
319         return new BufferedWriter( new FileWriter( file ) );
320     }
321
322     final public static BufferedWriter createBufferedWriter( final String name ) throws IOException {
323         return new BufferedWriter( new FileWriter( createFileForWriting( name ) ) );
324     }
325
326     final public static File createFileForWriting( final String name ) throws IOException {
327         final File file = new File( name );
328         if ( file.exists() ) {
329             throw new IOException( "[" + name + "] already exists" );
330         }
331         return file;
332     }
333
334     final public static PhylogenyParser createParserDependingFileContents( final File file,
335                                                                            final boolean phyloxml_validate_against_xsd )
336             throws FileNotFoundException, IOException {
337         PhylogenyParser parser = null;
338         final String first_line = ForesterUtil.getFirstLine( file ).trim().toLowerCase();
339         if ( first_line.startsWith( "<" ) ) {
340             parser = new PhyloXmlParser();
341             if ( phyloxml_validate_against_xsd ) {
342                 final ClassLoader cl = PhyloXmlParser.class.getClassLoader();
343                 final URL xsd_url = cl.getResource( ForesterConstants.LOCAL_PHYLOXML_XSD_RESOURCE );
344                 if ( xsd_url != null ) {
345                     ( ( PhyloXmlParser ) parser ).setValidateAgainstSchema( xsd_url.toString() );
346                 }
347                 else {
348                     if ( ForesterConstants.RELEASE ) {
349                         throw new RuntimeException( "failed to get URL for phyloXML XSD from jar file from ["
350                                 + ForesterConstants.LOCAL_PHYLOXML_XSD_RESOURCE + "]" );
351                     }
352                 }
353             }
354         }
355         else if ( ( first_line.startsWith( "nexus" ) ) || ( first_line.startsWith( "#nexus" ) )
356                 || ( first_line.startsWith( "# nexus" ) ) || ( first_line.startsWith( "begin" ) ) ) {
357             parser = new NexusPhylogeniesParser();
358         }
359         else {
360             parser = new NHXParser();
361         }
362         return parser;
363     }
364
365     final public static PhylogenyParser createParserDependingOnFileType( final File file,
366                                                                          final boolean phyloxml_validate_against_xsd )
367             throws FileNotFoundException, IOException {
368         PhylogenyParser parser = null;
369         parser = createParserDependingOnSuffix( file.getName(), phyloxml_validate_against_xsd );
370         if ( parser == null ) {
371             parser = createParserDependingFileContents( file, phyloxml_validate_against_xsd );
372         }
373         return parser;
374     }
375
376     /**
377      * Return null if it can not guess the parser to use based on name suffix.
378      * 
379      * @param filename
380      * @return
381      */
382     final public static PhylogenyParser createParserDependingOnSuffix( final String filename,
383                                                                        final boolean phyloxml_validate_against_xsd ) {
384         PhylogenyParser parser = null;
385         final String filename_lc = filename.toLowerCase();
386         if ( filename_lc.endsWith( ".tol" ) || filename_lc.endsWith( ".tolxml" ) || filename_lc.endsWith( ".tol.zip" ) ) {
387             parser = new TolParser();
388         }
389         else if ( filename_lc.endsWith( ".xml" ) || filename_lc.endsWith( ".px" ) || filename_lc.endsWith( "phyloxml" )
390                 || filename_lc.endsWith( ".zip" ) ) {
391             parser = new PhyloXmlParser();
392             if ( phyloxml_validate_against_xsd ) {
393                 final ClassLoader cl = PhyloXmlParser.class.getClassLoader();
394                 final URL xsd_url = cl.getResource( ForesterConstants.LOCAL_PHYLOXML_XSD_RESOURCE );
395                 if ( xsd_url != null ) {
396                     ( ( PhyloXmlParser ) parser ).setValidateAgainstSchema( xsd_url.toString() );
397                 }
398                 else {
399                     if ( ForesterConstants.RELEASE ) {
400                         throw new RuntimeException( "failed to get URL for phyloXML XSD from jar file from ["
401                                 + ForesterConstants.LOCAL_PHYLOXML_XSD_RESOURCE + "]" );
402                     }
403                 }
404             }
405         }
406         else if ( filename_lc.endsWith( ".nexus" ) || filename_lc.endsWith( ".nex" ) || filename_lc.endsWith( ".nx" ) ) {
407             parser = new NexusPhylogeniesParser();
408         }
409         else if ( filename_lc.endsWith( ".nhx" ) || filename_lc.endsWith( ".nh" ) || filename_lc.endsWith( ".newick" ) ) {
410             parser = new NHXParser();
411         }
412         return parser;
413     }
414
415     final public static PhylogenyParser createParserDependingOnUrlContents( final URL url,
416                                                                             final boolean phyloxml_validate_against_xsd )
417             throws FileNotFoundException, IOException {
418         final String lc_filename = url.getFile().toString().toLowerCase();
419         PhylogenyParser parser = createParserDependingOnSuffix( lc_filename, phyloxml_validate_against_xsd );
420         if ( ( parser != null ) && lc_filename.endsWith( ".zip" ) ) {
421             if ( parser instanceof PhyloXmlParser ) {
422                 ( ( PhyloXmlParser ) parser ).setZippedInputstream( true );
423             }
424             else if ( parser instanceof TolParser ) {
425                 ( ( TolParser ) parser ).setZippedInputstream( true );
426             }
427         }
428         if ( parser == null ) {
429             final String first_line = getFirstLine( url ).trim().toLowerCase();
430             if ( first_line.startsWith( "<" ) ) {
431                 parser = new PhyloXmlParser();
432                 if ( phyloxml_validate_against_xsd ) {
433                     final ClassLoader cl = PhyloXmlParser.class.getClassLoader();
434                     final URL xsd_url = cl.getResource( ForesterConstants.LOCAL_PHYLOXML_XSD_RESOURCE );
435                     if ( xsd_url != null ) {
436                         ( ( PhyloXmlParser ) parser ).setValidateAgainstSchema( xsd_url.toString() );
437                     }
438                     else {
439                         throw new RuntimeException( "failed to get URL for phyloXML XSD from jar file from ["
440                                 + ForesterConstants.LOCAL_PHYLOXML_XSD_RESOURCE + "]" );
441                     }
442                 }
443             }
444             else if ( ( first_line.startsWith( "nexus" ) ) || ( first_line.startsWith( "#nexus" ) )
445                     || ( first_line.startsWith( "# nexus" ) ) || ( first_line.startsWith( "begin" ) ) ) {
446                 parser = new NexusPhylogeniesParser();
447             }
448             else {
449                 parser = new NHXParser();
450             }
451         }
452         return parser;
453     }
454
455     final public static void ensurePresenceOfDate( final PhylogenyNode node ) {
456         if ( !node.getNodeData().isHasDate() ) {
457             node.getNodeData().setDate( new org.forester.phylogeny.data.Date() );
458         }
459     }
460
461     final public static void ensurePresenceOfDistribution( final PhylogenyNode node ) {
462         if ( !node.getNodeData().isHasDistribution() ) {
463             node.getNodeData().setDistribution( new Distribution( "" ) );
464         }
465     }
466
467     public static void ensurePresenceOfSequence( final PhylogenyNode node ) {
468         if ( !node.getNodeData().isHasSequence() ) {
469             node.getNodeData().setSequence( new Sequence() );
470         }
471     }
472
473     public static void ensurePresenceOfTaxonomy( final PhylogenyNode node ) {
474         if ( !node.getNodeData().isHasTaxonomy() ) {
475             node.getNodeData().setTaxonomy( new Taxonomy() );
476         }
477     }
478
479     /**
480      * Extracts a code if and only if:
481      * one and only one _, 
482      * shorter than 25, 
483      * no |, 
484      * no ., 
485      * if / present it has to be after the _, 
486      * if PFAM_STYLE_ONLY: / must be present,
487      * tax code can only contain uppercase letters and numbers,
488      * and must contain at least one uppercase letter.
489      * Return null if no code extractable.
490      * 
491      * @param name
492      * @param limit_to_five
493      * @return
494      */
495     public static String extractTaxonomyCodeFromNodeName( final String name,
496                                                           final boolean limit_to_five,
497                                                           final ForesterUtil.TAXONOMY_EXTRACTION taxonomy_extraction ) {
498         if ( ( name.indexOf( "_" ) > 0 )
499                 && ( name.length() < 25 )
500                 && ( name.lastIndexOf( "_" ) == name.indexOf( "_" ) )
501                 && ( name.indexOf( "|" ) < 0 )
502                 && ( name.indexOf( "." ) < 0 )
503                 && ( ( taxonomy_extraction != ForesterUtil.TAXONOMY_EXTRACTION.PFAM_STYLE_ONLY ) || ( name
504                         .indexOf( "/" ) >= 0 ) )
505                 && ( ( ( name.indexOf( "/" ) ) < 0 ) || ( name.indexOf( "/" ) > name.indexOf( "_" ) ) ) ) {
506             final String[] s = name.split( "[_/]" );
507             if ( s.length > 1 ) {
508                 String str = s[ 1 ];
509                 if ( limit_to_five ) {
510                     if ( str.length() > 5 ) {
511                         str = str.substring( 0, 5 );
512                     }
513                     else if ( ( str.length() < 5 ) && ( str.startsWith( "RAT" ) || str.startsWith( "PIG" ) ) ) {
514                         str = str.substring( 0, 3 );
515                     }
516                 }
517                 final Matcher letters_and_numbers = NHXParser.UC_LETTERS_NUMBERS_PATTERN.matcher( str );
518                 if ( !letters_and_numbers.matches() ) {
519                     return null;
520                 }
521                 final Matcher numbers_only = NHXParser.NUMBERS_ONLY_PATTERN.matcher( str );
522                 if ( numbers_only.matches() ) {
523                     return null;
524                 }
525                 return str;
526             }
527         }
528         return null;
529     }
530
531     public static void fatalError( final String prg_name, final String message ) {
532         System.err.println();
533         System.err.println( "[" + prg_name + "] > " + message );
534         System.err.println();
535         System.exit( -1 );
536     }
537
538     public static String[] file2array( final File file ) throws IOException {
539         final List<String> list = file2list( file );
540         final String[] ary = new String[ list.size() ];
541         int i = 0;
542         for( final String s : list ) {
543             ary[ i++ ] = s;
544         }
545         return ary;
546     }
547
548     final public static List<String> file2list( final File file ) throws IOException {
549         final List<String> list = new ArrayList<String>();
550         final BufferedReader in = new BufferedReader( new FileReader( file ) );
551         String str;
552         while ( ( str = in.readLine() ) != null ) {
553             str = str.trim();
554             if ( ( str.length() > 0 ) && !str.startsWith( "#" ) ) {
555                 for( final String s : splitString( str ) ) {
556                     list.add( s );
557                 }
558             }
559         }
560         in.close();
561         return list;
562     }
563
564     final public static SortedSet<String> file2set( final File file ) throws IOException {
565         final SortedSet<String> set = new TreeSet<String>();
566         final BufferedReader in = new BufferedReader( new FileReader( file ) );
567         String str;
568         while ( ( str = in.readLine() ) != null ) {
569             str = str.trim();
570             if ( ( str.length() > 0 ) && !str.startsWith( "#" ) ) {
571                 for( final String s : splitString( str ) ) {
572                     set.add( s );
573                 }
574             }
575         }
576         in.close();
577         return set;
578     }
579
580     final public static String getCurrentDateTime() {
581         final DateFormat format = new SimpleDateFormat( "yyyy/MM/dd HH:mm:ss" );
582         return format.format( new Date() );
583     }
584
585     final public static String getFileSeparator() {
586         return ForesterUtil.FILE_SEPARATOR;
587     }
588
589     final public static String getFirstLine( final Object source ) throws FileNotFoundException, IOException {
590         BufferedReader reader = null;
591         if ( source instanceof File ) {
592             final File f = ( File ) source;
593             if ( !f.exists() ) {
594                 throw new IOException( "[" + f.getAbsolutePath() + "] does not exist" );
595             }
596             else if ( !f.isFile() ) {
597                 throw new IOException( "[" + f.getAbsolutePath() + "] is not a file" );
598             }
599             else if ( !f.canRead() ) {
600                 throw new IOException( "[" + f.getAbsolutePath() + "] is not a readable" );
601             }
602             reader = new BufferedReader( new FileReader( f ) );
603         }
604         else if ( source instanceof InputStream ) {
605             reader = new BufferedReader( new InputStreamReader( ( InputStream ) source ) );
606         }
607         else if ( source instanceof String ) {
608             reader = new BufferedReader( new StringReader( ( String ) source ) );
609         }
610         else if ( source instanceof StringBuffer ) {
611             reader = new BufferedReader( new StringReader( source.toString() ) );
612         }
613         else if ( source instanceof URL ) {
614             reader = new BufferedReader( new InputStreamReader( ( ( URL ) source ).openStream() ) );
615         }
616         else {
617             throw new IllegalArgumentException( "dont know how to read [" + source.getClass() + "]" );
618         }
619         String line;
620         while ( ( line = reader.readLine() ) != null ) {
621             line = line.trim();
622             if ( !ForesterUtil.isEmpty( line ) ) {
623                 if ( reader != null ) {
624                     reader.close();
625                 }
626                 return line;
627             }
628         }
629         if ( reader != null ) {
630             reader.close();
631         }
632         return line;
633     }
634
635     final public static String getLineSeparator() {
636         return ForesterUtil.LINE_SEPARATOR;
637     }
638
639     /**
640      * Returns all custom data tag names of this Phylogeny as Hashtable. Tag
641      * names are keys, values are Boolean set to false.
642      */
643     final public static Hashtable<String, Boolean> getPropertyRefs( final Phylogeny phylogeny ) {
644         final Hashtable<String, Boolean> ht = new Hashtable<String, Boolean>();
645         if ( phylogeny.isEmpty() ) {
646             return ht;
647         }
648         for( final PhylogenyNodeIterator iter = phylogeny.iteratorPreorder(); iter.hasNext(); ) {
649             final PhylogenyNode current_node = iter.next();
650             if ( current_node.getNodeData().isHasProperties() ) {
651                 final String[] tags = current_node.getNodeData().getProperties().getPropertyRefs();
652                 for( int i = 0; i < tags.length; ++i ) {
653                     ht.put( tags[ i ], new Boolean( false ) );
654                 }
655             }
656         }
657         return ht;
658     }
659
660     final public static void increaseCountingMap( final Map<String, Integer> counting_map, final String item_name ) {
661         if ( !counting_map.containsKey( item_name ) ) {
662             counting_map.put( item_name, 1 );
663         }
664         else {
665             counting_map.put( item_name, counting_map.get( item_name ) + 1 );
666         }
667     }
668
669     final static public boolean isAllNonEmptyInternalLabelsArePositiveNumbers( final Phylogeny phy ) {
670         final PhylogenyNodeIterator it = phy.iteratorPostorder();
671         while ( it.hasNext() ) {
672             final PhylogenyNode n = it.next();
673             if ( !n.isRoot() && !n.isExternal() ) {
674                 if ( !ForesterUtil.isEmpty( n.getName() ) ) {
675                     double d = -1.0;
676                     try {
677                         d = Double.parseDouble( n.getName() );
678                     }
679                     catch ( final Exception e ) {
680                         d = -1.0;
681                     }
682                     if ( d < 0.0 ) {
683                         return false;
684                     }
685                 }
686             }
687         }
688         return true;
689     }
690
691     final public static boolean isEmpty( final String s ) {
692         return ( ( s == null ) || ( s.length() < 1 ) );
693     }
694
695     final public static boolean isEqual( final double a, final double b ) {
696         return ( ( Math.abs( a - b ) ) < ZERO_DIFF );
697     }
698
699     final public static boolean isEven( final int n ) {
700         return n % 2 == 0;
701     }
702
703     final static public boolean isHasAtLeastNodeWithEvent( final Phylogeny phy ) {
704         final PhylogenyNodeIterator it = phy.iteratorPostorder();
705         while ( it.hasNext() ) {
706             if ( it.next().getNodeData().isHasEvent() ) {
707                 return true;
708             }
709         }
710         return false;
711     }
712
713     /**
714      * Returns true if at least one branch has a length larger than zero.
715      * 
716      * 
717      * @param phy
718      */
719     final static public boolean isHasAtLeastOneBranchLengthLargerThanZero( final Phylogeny phy ) {
720         final PhylogenyNodeIterator it = phy.iteratorPostorder();
721         while ( it.hasNext() ) {
722             if ( it.next().getDistanceToParent() > 0.0 ) {
723                 return true;
724             }
725         }
726         return false;
727     }
728
729     final static public boolean isHasAtLeastOneBranchWithSupportValues( final Phylogeny phy ) {
730         final PhylogenyNodeIterator it = phy.iteratorPostorder();
731         while ( it.hasNext() ) {
732             if ( it.next().getBranchData().isHasConfidences() ) {
733                 return true;
734             }
735         }
736         return false;
737     }
738
739     /**
740      * This determines whether String[] a and String[] b have at least one
741      * String in common (intersect). Returns false if at least one String[] is
742      * null or empty.
743      * 
744      * @param a
745      *            a String[] b a String[]
746      * @return true if both a and b or not empty or null and contain at least
747      *         one element in common false otherwise
748      */
749     final public static boolean isIntersecting( final String[] a, final String[] b ) {
750         if ( ( a == null ) || ( b == null ) ) {
751             return false;
752         }
753         if ( ( a.length < 1 ) || ( b.length < 1 ) ) {
754             return false;
755         }
756         for( int i = 0; i < a.length; ++i ) {
757             final String ai = a[ i ];
758             for( int j = 0; j < b.length; ++j ) {
759                 if ( ( ai != null ) && ( b[ j ] != null ) && ai.equals( b[ j ] ) ) {
760                     return true;
761                 }
762             }
763         }
764         return false;
765     }
766
767     final public static double isLargerOrEqualToZero( final double d ) {
768         if ( d > 0.0 ) {
769             return d;
770         }
771         else {
772             return 0.0;
773         }
774     }
775
776     final public static boolean isNull( final BigDecimal s ) {
777         return ( ( s == null ) || ( s.compareTo( NULL_BD ) == 0 ) );
778     }
779
780     final public static String isReadableFile( final File f ) {
781         if ( !f.exists() ) {
782             return "file [" + f + "] does not exist";
783         }
784         if ( f.isDirectory() ) {
785             return "[" + f + "] is a directory";
786         }
787         if ( !f.isFile() ) {
788             return "[" + f + "] is not a file";
789         }
790         if ( !f.canRead() ) {
791             return "file [" + f + "] is not readable";
792         }
793         if ( f.length() < 1 ) {
794             return "file [" + f + "] is empty";
795         }
796         return null;
797     }
798
799     final public static String isReadableFile( final String s ) {
800         return isReadableFile( new File( s ) );
801     }
802
803     final public static String isWritableFile( final File f ) {
804         if ( f.isDirectory() ) {
805             return "[" + f + "] is a directory";
806         }
807         if ( f.exists() ) {
808             return "[" + f + "] already exists";
809         }
810         return null;
811     }
812
813     /**
814      * Helper for method "stringToColor".
815      * <p>
816      * (Last modified: 12/20/03)
817      */
818     final public static int limitRangeForColor( int i ) {
819         if ( i > 255 ) {
820             i = 255;
821         }
822         else if ( i < 0 ) {
823             i = 0;
824         }
825         return i;
826     }
827
828     final public static SortedMap<Object, Integer> listToSortedCountsMap( final List list ) {
829         final SortedMap<Object, Integer> map = new TreeMap<Object, Integer>();
830         for( final Object key : list ) {
831             if ( !map.containsKey( key ) ) {
832                 map.put( key, 1 );
833             }
834             else {
835                 map.put( key, map.get( key ) + 1 );
836             }
837         }
838         return map;
839     }
840
841     final public static StringBuffer mapToStringBuffer( final Map map, final String key_value_separator ) {
842         final StringBuffer sb = new StringBuffer();
843         for( final Iterator iter = map.keySet().iterator(); iter.hasNext(); ) {
844             final Object key = iter.next();
845             sb.append( key.toString() );
846             sb.append( key_value_separator );
847             sb.append( map.get( key ).toString() );
848             sb.append( ForesterUtil.getLineSeparator() );
849         }
850         return sb;
851     }
852
853     final public static String normalizeString( final String s,
854                                                 final int length,
855                                                 final boolean left_pad,
856                                                 final char pad_char ) {
857         if ( s.length() > length ) {
858             return s.substring( 0, length );
859         }
860         else {
861             final StringBuffer pad = new StringBuffer( length - s.length() );
862             for( int i = 0; i < ( length - s.length() ); ++i ) {
863                 pad.append( pad_char );
864             }
865             if ( left_pad ) {
866                 return pad + s;
867             }
868             else {
869                 return s + pad;
870             }
871         }
872     }
873
874     final public static BufferedReader obtainReader( final Object source ) throws IOException, FileNotFoundException {
875         BufferedReader reader = null;
876         if ( source instanceof File ) {
877             final File f = ( File ) source;
878             if ( !f.exists() ) {
879                 throw new IOException( "\"" + f.getAbsolutePath() + "\" does not exist" );
880             }
881             else if ( !f.isFile() ) {
882                 throw new IOException( "\"" + f.getAbsolutePath() + "\" is not a file" );
883             }
884             else if ( !f.canRead() ) {
885                 throw new IOException( "\"" + f.getAbsolutePath() + "\" is not a readable" );
886             }
887             reader = new BufferedReader( new FileReader( f ) );
888         }
889         else if ( source instanceof InputStream ) {
890             reader = new BufferedReader( new InputStreamReader( ( InputStream ) source ) );
891         }
892         else if ( source instanceof String ) {
893             reader = new BufferedReader( new StringReader( ( String ) source ) );
894         }
895         else if ( source instanceof StringBuffer ) {
896             reader = new BufferedReader( new StringReader( source.toString() ) );
897         }
898         else {
899             throw new IllegalArgumentException( "attempt to parse object of type [" + source.getClass()
900                     + "] (can only parse objects of type File, InputStream, String, or StringBuffer)" );
901         }
902         return reader;
903     }
904
905     final public static StringBuffer pad( final double number, final int size, final char pad, final boolean left_pad ) {
906         return pad( new StringBuffer( number + "" ), size, pad, left_pad );
907     }
908
909     final public static StringBuffer pad( final String string, final int size, final char pad, final boolean left_pad ) {
910         return pad( new StringBuffer( string ), size, pad, left_pad );
911     }
912
913     final public static StringBuffer pad( final StringBuffer string,
914                                           final int size,
915                                           final char pad,
916                                           final boolean left_pad ) {
917         final StringBuffer padding = new StringBuffer();
918         final int s = size - string.length();
919         if ( s < 1 ) {
920             return new StringBuffer( string.substring( 0, size ) );
921         }
922         for( int i = 0; i < s; ++i ) {
923             padding.append( pad );
924         }
925         if ( left_pad ) {
926             return padding.append( string );
927         }
928         else {
929             return string.append( padding );
930         }
931     }
932
933     final public static double parseDouble( final String str ) throws ParseException {
934         if ( ForesterUtil.isEmpty( str ) ) {
935             return 0.0;
936         }
937         return Double.parseDouble( str );
938     }
939
940     final public static int parseInt( final String str ) throws ParseException {
941         if ( ForesterUtil.isEmpty( str ) ) {
942             return 0;
943         }
944         return Integer.parseInt( str );
945     }
946
947     final public static void postOrderRelabelInternalNodes( final Phylogeny phylogeny, final int starting_number ) {
948         int i = starting_number;
949         for( final PhylogenyNodeIterator it = phylogeny.iteratorPostorder(); it.hasNext(); ) {
950             final PhylogenyNode node = it.next();
951             if ( !node.isExternal() ) {
952                 node.setName( String.valueOf( i++ ) );
953             }
954         }
955     }
956
957     final public static void printArray( final Object[] a ) {
958         for( int i = 0; i < a.length; ++i ) {
959             System.out.println( "[" + i + "]=" + a[ i ] );
960         }
961     }
962
963     final public static void printCountingMap( final Map<String, Integer> counting_map ) {
964         for( final String key : counting_map.keySet() ) {
965             System.out.println( key + ": " + counting_map.get( key ) );
966         }
967     }
968
969     final public static void printErrorMessage( final String prg_name, final String message ) {
970         System.out.println( "[" + prg_name + "] > error: " + message );
971     }
972
973     final public static void printProgramInformation( final String prg_name, final String prg_version, final String date ) {
974         final int l = prg_name.length() + prg_version.length() + date.length() + 4;
975         System.out.println();
976         System.out.println( prg_name + " " + prg_version + " (" + date + ")" );
977         for( int i = 0; i < l; ++i ) {
978             System.out.print( "_" );
979         }
980         System.out.println();
981     }
982
983     final public static void printProgramInformation( final String prg_name,
984                                                       final String prg_version,
985                                                       final String date,
986                                                       final String email,
987                                                       final String www ) {
988         final int l = prg_name.length() + prg_version.length() + date.length() + 4;
989         System.out.println();
990         System.out.println( prg_name + " " + prg_version + " (" + date + ")" );
991         for( int i = 0; i < l; ++i ) {
992             System.out.print( "_" );
993         }
994         System.out.println();
995         System.out.println();
996         System.out.println( "WWW    : " + www );
997         System.out.println( "Contact: " + email );
998         if ( !ForesterUtil.isEmpty( ForesterUtil.JAVA_VERSION ) && !ForesterUtil.isEmpty( ForesterUtil.JAVA_VENDOR ) ) {
999             System.out.println();
1000             System.out.println( "[running on Java " + ForesterUtil.JAVA_VERSION + " " + ForesterUtil.JAVA_VENDOR + "]" );
1001         }
1002         System.out.println();
1003     }
1004
1005     final public static void printWarningMessage( final String prg_name, final String message ) {
1006         System.out.println( "[" + prg_name + "] > warning: " + message );
1007     }
1008
1009     final public static void programMessage( final String prg_name, final String message ) {
1010         System.out.println( "[" + prg_name + "] > " + message );
1011     }
1012
1013     final public static String removeSuffix( final String file_name ) {
1014         final int i = file_name.lastIndexOf( '.' );
1015         if ( i > 1 ) {
1016             return file_name.substring( 0, i );
1017         }
1018         return file_name;
1019     }
1020
1021     /**
1022      * Removes all white space from String s.
1023      * 
1024      * @return String s with white space removed
1025      */
1026     final public static String removeWhiteSpace( String s ) {
1027         int i;
1028         for( i = 0; i <= s.length() - 1; i++ ) {
1029             if ( ( s.charAt( i ) == ' ' ) || ( s.charAt( i ) == '\t' ) || ( s.charAt( i ) == '\n' )
1030                     || ( s.charAt( i ) == '\r' ) ) {
1031                 s = s.substring( 0, i ) + s.substring( i + 1 );
1032                 i--;
1033             }
1034         }
1035         return s;
1036     }
1037
1038     final public static boolean isContainsParanthesesableNhCharacter( final String nh ) {
1039         return PARANTHESESABLE_NH_CHARS_PATTERN.matcher( nh ).find();
1040     }
1041
1042     final public static String replaceIllegalNhCharacters( final String nh ) {
1043         if ( nh == null ) {
1044             return "";
1045         }
1046         return nh.trim().replaceAll( "[\\[\\]:]+", "_" );
1047     }
1048
1049     final public static String replaceIllegalNhxCharacters( final String nhx ) {
1050         if ( nhx == null ) {
1051             return "";
1052         }
1053         return nhx.trim().replaceAll( "[\\[\\](),:;\\s]+", "_" );
1054     }
1055
1056     final public static double round( final double value, final int decimal_place ) {
1057         BigDecimal bd = new BigDecimal( value );
1058         bd = bd.setScale( decimal_place, BigDecimal.ROUND_HALF_UP );
1059         return bd.doubleValue();
1060     }
1061
1062     /**
1063      * Rounds d to an int.
1064      */
1065     final public static int roundToInt( final double d ) {
1066         return ( int ) ( d + 0.5 );
1067     }
1068
1069     final public static int roundToInt( final float f ) {
1070         return ( int ) ( f + 0.5f );
1071     }
1072
1073     final public static String sanitizeString( final String s ) {
1074         if ( s == null ) {
1075             return "";
1076         }
1077         else {
1078             return s.trim();
1079         }
1080     }
1081
1082     final private static String[] splitString( final String str ) {
1083         final String regex = "[\\s;,]+";
1084         return str.split( regex );
1085     }
1086
1087     final public static String stringArrayToString( final String[] a ) {
1088         final StringBuffer sb = new StringBuffer();
1089         if ( ( a != null ) && ( a.length > 0 ) ) {
1090             for( int i = 0; i < a.length - 1; ++i ) {
1091                 sb.append( a[ i ] + ", " );
1092             }
1093             sb.append( a[ a.length - 1 ] );
1094         }
1095         return sb.toString();
1096     }
1097
1098     final static public void transferInternalNamesToBootstrapSupport( final Phylogeny phy ) {
1099         final PhylogenyNodeIterator it = phy.iteratorPostorder();
1100         while ( it.hasNext() ) {
1101             final PhylogenyNode n = it.next();
1102             if ( !n.isExternal() && !ForesterUtil.isEmpty( n.getName() ) ) {
1103                 double value = -1;
1104                 try {
1105                     value = Double.parseDouble( n.getName() );
1106                 }
1107                 catch ( final NumberFormatException e ) {
1108                     throw new IllegalArgumentException( "failed to parse number from [" + n.getName() + "]: "
1109                             + e.getLocalizedMessage() );
1110                 }
1111                 if ( value >= 0.0 ) {
1112                     n.getBranchData().addConfidence( new Confidence( value, "bootstrap" ) );
1113                     n.setName( "" );
1114                 }
1115             }
1116         }
1117     }
1118
1119     final static public void transferInternalNodeNamesToConfidence( final Phylogeny phy ) {
1120         final PhylogenyNodeIterator it = phy.iteratorPostorder();
1121         while ( it.hasNext() ) {
1122             final PhylogenyNode n = it.next();
1123             if ( !n.isRoot() && !n.isExternal() && !n.getBranchData().isHasConfidences() ) {
1124                 if ( !ForesterUtil.isEmpty( n.getName() ) ) {
1125                     double d = -1.0;
1126                     try {
1127                         d = Double.parseDouble( n.getName() );
1128                     }
1129                     catch ( final Exception e ) {
1130                         d = -1.0;
1131                     }
1132                     if ( d >= 0.0 ) {
1133                         n.getBranchData().addConfidence( new Confidence( d, "" ) );
1134                         n.setName( "" );
1135                     }
1136                 }
1137             }
1138         }
1139     }
1140
1141     final static public void transferNodeNameToField( final Phylogeny phy, final PhylogenyNodeField field ) {
1142         final PhylogenyNodeIterator it = phy.iteratorPostorder();
1143         while ( it.hasNext() ) {
1144             final PhylogenyNode n = it.next();
1145             final String name = n.getName().trim();
1146             if ( !ForesterUtil.isEmpty( name ) ) {
1147                 switch ( field ) {
1148                     case TAXONOMY_CODE:
1149                         //temp hack
1150                         //                        if ( name.length() > 5 ) {
1151                         //                            n.setName( "" );
1152                         //                            if ( !n.getNodeData().isHasTaxonomy() ) {
1153                         //                                n.getNodeData().setTaxonomy( new Taxonomy() );
1154                         //                            }
1155                         //                            n.getNodeData().getTaxonomy().setScientificName( name );
1156                         //                            break;
1157                         //                        }
1158                         //
1159                         n.setName( "" );
1160                         PhylogenyMethods.setTaxonomyCode( n, name );
1161                         break;
1162                     case TAXONOMY_SCIENTIFIC_NAME:
1163                         n.setName( "" );
1164                         if ( !n.getNodeData().isHasTaxonomy() ) {
1165                             n.getNodeData().setTaxonomy( new Taxonomy() );
1166                         }
1167                         n.getNodeData().getTaxonomy().setScientificName( name );
1168                         break;
1169                     case TAXONOMY_COMMON_NAME:
1170                         n.setName( "" );
1171                         if ( !n.getNodeData().isHasTaxonomy() ) {
1172                             n.getNodeData().setTaxonomy( new Taxonomy() );
1173                         }
1174                         n.getNodeData().getTaxonomy().setCommonName( name );
1175                         break;
1176                     case SEQUENCE_SYMBOL:
1177                         n.setName( "" );
1178                         if ( !n.getNodeData().isHasSequence() ) {
1179                             n.getNodeData().setSequence( new Sequence() );
1180                         }
1181                         n.getNodeData().getSequence().setSymbol( name );
1182                         break;
1183                     case SEQUENCE_NAME:
1184                         n.setName( "" );
1185                         if ( !n.getNodeData().isHasSequence() ) {
1186                             n.getNodeData().setSequence( new Sequence() );
1187                         }
1188                         n.getNodeData().getSequence().setName( name );
1189                         break;
1190                     case TAXONOMY_ID_UNIPROT_1: {
1191                         if ( !n.getNodeData().isHasTaxonomy() ) {
1192                             n.getNodeData().setTaxonomy( new Taxonomy() );
1193                         }
1194                         String id = name;
1195                         final int i = name.indexOf( '_' );
1196                         if ( i > 0 ) {
1197                             id = name.substring( 0, i );
1198                         }
1199                         else {
1200                             n.setName( "" );
1201                         }
1202                         n.getNodeData().getTaxonomy()
1203                                 .setIdentifier( new Identifier( id, PhyloXmlUtil.UNIPROT_TAX_PROVIDER ) );
1204                         break;
1205                     }
1206                     case TAXONOMY_ID_UNIPROT_2: {
1207                         if ( !n.getNodeData().isHasTaxonomy() ) {
1208                             n.getNodeData().setTaxonomy( new Taxonomy() );
1209                         }
1210                         String id = name;
1211                         final int i = name.indexOf( '_' );
1212                         if ( i > 0 ) {
1213                             id = name.substring( i + 1, name.length() );
1214                         }
1215                         else {
1216                             n.setName( "" );
1217                         }
1218                         n.getNodeData().getTaxonomy()
1219                                 .setIdentifier( new Identifier( id, PhyloXmlUtil.UNIPROT_TAX_PROVIDER ) );
1220                         break;
1221                     }
1222                 }
1223             }
1224         }
1225     }
1226
1227     final public static void unexpectedFatalError( final String prg_name, final Exception e ) {
1228         System.err.println();
1229         System.err.println( "[" + prg_name
1230                 + "] > unexpected error (Should not have occured! Please contact program author(s).)" );
1231         e.printStackTrace( System.err );
1232         System.err.println();
1233         System.exit( -1 );
1234     }
1235
1236     final public static void unexpectedFatalError( final String prg_name, final String message ) {
1237         System.err.println();
1238         System.err.println( "[" + prg_name
1239                 + "] > unexpected error. Should not have occured! Please contact program author(s)." );
1240         System.err.println( message );
1241         System.err.println();
1242         System.exit( -1 );
1243     }
1244
1245     final public static void unexpectedFatalError( final String prg_name, final String message, final Exception e ) {
1246         System.err.println();
1247         System.err.println( "[" + prg_name
1248                 + "] > unexpected error. Should not have occured! Please contact program author(s)." );
1249         System.err.println( message );
1250         e.printStackTrace( System.err );
1251         System.err.println();
1252         System.exit( -1 );
1253     }
1254
1255     public final static String wordWrap( final String str, final int width ) {
1256         final StringBuilder sb = new StringBuilder( str );
1257         int start = 0;
1258         int ls = -1;
1259         int i = 0;
1260         while ( i < sb.length() ) {
1261             if ( sb.charAt( i ) == ' ' ) {
1262                 ls = i;
1263             }
1264             if ( sb.charAt( i ) == '\n' ) {
1265                 ls = -1;
1266                 start = i + 1;
1267             }
1268             if ( i > start + width - 1 ) {
1269                 if ( ls != -1 ) {
1270                     sb.setCharAt( ls, '\n' );
1271                     start = ls + 1;
1272                     ls = -1;
1273                 }
1274                 else {
1275                     sb.insert( i, '\n' );
1276                     start = i + 1;
1277                 }
1278             }
1279             i++;
1280         }
1281         return sb.toString();
1282     }
1283
1284     public static enum PhylogenyNodeField {
1285         CLADE_NAME,
1286         TAXONOMY_CODE,
1287         TAXONOMY_SCIENTIFIC_NAME,
1288         TAXONOMY_COMMON_NAME,
1289         SEQUENCE_SYMBOL,
1290         SEQUENCE_NAME,
1291         TAXONOMY_ID_UNIPROT_1,
1292         TAXONOMY_ID_UNIPROT_2;
1293     }
1294
1295     public static enum TAXONOMY_EXTRACTION {
1296         NO, YES, PFAM_STYLE_ONLY;
1297     }
1298 }