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