111dc403da6c6f2bb6ddfe8aea5d8c6075809444
[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 short roundToShort( final double d ) {
1070         return ( short ) ( d + 0.5 );
1071     }
1072
1073     final public static int roundToInt( final float f ) {
1074         return ( int ) ( f + 0.5f );
1075     }
1076
1077     final public static String sanitizeString( final String s ) {
1078         if ( s == null ) {
1079             return "";
1080         }
1081         else {
1082             return s.trim();
1083         }
1084     }
1085
1086     final private static String[] splitString( final String str ) {
1087         final String regex = "[\\s;,]+";
1088         return str.split( regex );
1089     }
1090
1091     final public static String stringArrayToString( final String[] a ) {
1092         final StringBuffer sb = new StringBuffer();
1093         if ( ( a != null ) && ( a.length > 0 ) ) {
1094             for( int i = 0; i < a.length - 1; ++i ) {
1095                 sb.append( a[ i ] + ", " );
1096             }
1097             sb.append( a[ a.length - 1 ] );
1098         }
1099         return sb.toString();
1100     }
1101
1102     final static public void transferInternalNamesToBootstrapSupport( final Phylogeny phy ) {
1103         final PhylogenyNodeIterator it = phy.iteratorPostorder();
1104         while ( it.hasNext() ) {
1105             final PhylogenyNode n = it.next();
1106             if ( !n.isExternal() && !ForesterUtil.isEmpty( n.getName() ) ) {
1107                 double value = -1;
1108                 try {
1109                     value = Double.parseDouble( n.getName() );
1110                 }
1111                 catch ( final NumberFormatException e ) {
1112                     throw new IllegalArgumentException( "failed to parse number from [" + n.getName() + "]: "
1113                             + e.getLocalizedMessage() );
1114                 }
1115                 if ( value >= 0.0 ) {
1116                     n.getBranchData().addConfidence( new Confidence( value, "bootstrap" ) );
1117                     n.setName( "" );
1118                 }
1119             }
1120         }
1121     }
1122
1123     final static public void transferInternalNodeNamesToConfidence( final Phylogeny phy ) {
1124         final PhylogenyNodeIterator it = phy.iteratorPostorder();
1125         while ( it.hasNext() ) {
1126             final PhylogenyNode n = it.next();
1127             if ( !n.isRoot() && !n.isExternal() && !n.getBranchData().isHasConfidences() ) {
1128                 if ( !ForesterUtil.isEmpty( n.getName() ) ) {
1129                     double d = -1.0;
1130                     try {
1131                         d = Double.parseDouble( n.getName() );
1132                     }
1133                     catch ( final Exception e ) {
1134                         d = -1.0;
1135                     }
1136                     if ( d >= 0.0 ) {
1137                         n.getBranchData().addConfidence( new Confidence( d, "" ) );
1138                         n.setName( "" );
1139                     }
1140                 }
1141             }
1142         }
1143     }
1144
1145     final static public void transferNodeNameToField( final Phylogeny phy, final PhylogenyNodeField field ) {
1146         final PhylogenyNodeIterator it = phy.iteratorPostorder();
1147         while ( it.hasNext() ) {
1148             final PhylogenyNode n = it.next();
1149             final String name = n.getName().trim();
1150             if ( !ForesterUtil.isEmpty( name ) ) {
1151                 switch ( field ) {
1152                     case TAXONOMY_CODE:
1153                         //temp hack
1154                         //                        if ( name.length() > 5 ) {
1155                         //                            n.setName( "" );
1156                         //                            if ( !n.getNodeData().isHasTaxonomy() ) {
1157                         //                                n.getNodeData().setTaxonomy( new Taxonomy() );
1158                         //                            }
1159                         //                            n.getNodeData().getTaxonomy().setScientificName( name );
1160                         //                            break;
1161                         //                        }
1162                         //
1163                         n.setName( "" );
1164                         PhylogenyMethods.setTaxonomyCode( n, name );
1165                         break;
1166                     case TAXONOMY_SCIENTIFIC_NAME:
1167                         n.setName( "" );
1168                         if ( !n.getNodeData().isHasTaxonomy() ) {
1169                             n.getNodeData().setTaxonomy( new Taxonomy() );
1170                         }
1171                         n.getNodeData().getTaxonomy().setScientificName( name );
1172                         break;
1173                     case TAXONOMY_COMMON_NAME:
1174                         n.setName( "" );
1175                         if ( !n.getNodeData().isHasTaxonomy() ) {
1176                             n.getNodeData().setTaxonomy( new Taxonomy() );
1177                         }
1178                         n.getNodeData().getTaxonomy().setCommonName( name );
1179                         break;
1180                     case SEQUENCE_SYMBOL:
1181                         n.setName( "" );
1182                         if ( !n.getNodeData().isHasSequence() ) {
1183                             n.getNodeData().setSequence( new Sequence() );
1184                         }
1185                         n.getNodeData().getSequence().setSymbol( name );
1186                         break;
1187                     case SEQUENCE_NAME:
1188                         n.setName( "" );
1189                         if ( !n.getNodeData().isHasSequence() ) {
1190                             n.getNodeData().setSequence( new Sequence() );
1191                         }
1192                         n.getNodeData().getSequence().setName( name );
1193                         break;
1194                     case TAXONOMY_ID_UNIPROT_1: {
1195                         if ( !n.getNodeData().isHasTaxonomy() ) {
1196                             n.getNodeData().setTaxonomy( new Taxonomy() );
1197                         }
1198                         String id = name;
1199                         final int i = name.indexOf( '_' );
1200                         if ( i > 0 ) {
1201                             id = name.substring( 0, i );
1202                         }
1203                         else {
1204                             n.setName( "" );
1205                         }
1206                         n.getNodeData().getTaxonomy()
1207                                 .setIdentifier( new Identifier( id, PhyloXmlUtil.UNIPROT_TAX_PROVIDER ) );
1208                         break;
1209                     }
1210                     case TAXONOMY_ID_UNIPROT_2: {
1211                         if ( !n.getNodeData().isHasTaxonomy() ) {
1212                             n.getNodeData().setTaxonomy( new Taxonomy() );
1213                         }
1214                         String id = name;
1215                         final int i = name.indexOf( '_' );
1216                         if ( i > 0 ) {
1217                             id = name.substring( i + 1, name.length() );
1218                         }
1219                         else {
1220                             n.setName( "" );
1221                         }
1222                         n.getNodeData().getTaxonomy()
1223                                 .setIdentifier( new Identifier( id, PhyloXmlUtil.UNIPROT_TAX_PROVIDER ) );
1224                         break;
1225                     }
1226                 }
1227             }
1228         }
1229     }
1230
1231     final public static void unexpectedFatalError( final String prg_name, final Exception e ) {
1232         System.err.println();
1233         System.err.println( "[" + prg_name
1234                 + "] > unexpected error (Should not have occured! Please contact program author(s).)" );
1235         e.printStackTrace( System.err );
1236         System.err.println();
1237         System.exit( -1 );
1238     }
1239
1240     final public static void unexpectedFatalError( final String prg_name, final String message ) {
1241         System.err.println();
1242         System.err.println( "[" + prg_name
1243                 + "] > unexpected error. Should not have occured! Please contact program author(s)." );
1244         System.err.println( message );
1245         System.err.println();
1246         System.exit( -1 );
1247     }
1248
1249     final public static void unexpectedFatalError( final String prg_name, final String message, final Exception e ) {
1250         System.err.println();
1251         System.err.println( "[" + prg_name
1252                 + "] > unexpected error. Should not have occured! Please contact program author(s)." );
1253         System.err.println( message );
1254         e.printStackTrace( System.err );
1255         System.err.println();
1256         System.exit( -1 );
1257     }
1258
1259     public final static String wordWrap( final String str, final int width ) {
1260         final StringBuilder sb = new StringBuilder( str );
1261         int start = 0;
1262         int ls = -1;
1263         int i = 0;
1264         while ( i < sb.length() ) {
1265             if ( sb.charAt( i ) == ' ' ) {
1266                 ls = i;
1267             }
1268             if ( sb.charAt( i ) == '\n' ) {
1269                 ls = -1;
1270                 start = i + 1;
1271             }
1272             if ( i > start + width - 1 ) {
1273                 if ( ls != -1 ) {
1274                     sb.setCharAt( ls, '\n' );
1275                     start = ls + 1;
1276                     ls = -1;
1277                 }
1278                 else {
1279                     sb.insert( i, '\n' );
1280                     start = i + 1;
1281                 }
1282             }
1283             i++;
1284         }
1285         return sb.toString();
1286     }
1287
1288     public static enum PhylogenyNodeField {
1289         CLADE_NAME,
1290         TAXONOMY_CODE,
1291         TAXONOMY_SCIENTIFIC_NAME,
1292         TAXONOMY_COMMON_NAME,
1293         SEQUENCE_SYMBOL,
1294         SEQUENCE_NAME,
1295         TAXONOMY_ID_UNIPROT_1,
1296         TAXONOMY_ID_UNIPROT_2;
1297     }
1298
1299     public static enum TAXONOMY_EXTRACTION {
1300         NO, YES, PFAM_STYLE_ONLY;
1301     }
1302 }