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