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