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