Merge branch 'master' of https://github.com/cmzmasek/forester.git into JalviewIntegration
[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.FilenameFilter;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.io.InputStreamReader;
41 import java.io.StringReader;
42 import java.io.Writer;
43 import java.math.BigDecimal;
44 import java.net.URL;
45 import java.net.URLConnection;
46 import java.security.KeyManagementException;
47 import java.security.NoSuchAlgorithmException;
48 import java.text.DateFormat;
49 import java.text.DecimalFormat;
50 import java.text.DecimalFormatSymbols;
51 import java.text.NumberFormat;
52 import java.text.ParseException;
53 import java.text.SimpleDateFormat;
54 import java.util.ArrayList;
55 import java.util.Collection;
56 import java.util.Date;
57 import java.util.List;
58 import java.util.Map;
59 import java.util.Map.Entry;
60 import java.util.Set;
61 import java.util.SortedMap;
62 import java.util.SortedSet;
63 import java.util.TreeMap;
64 import java.util.TreeSet;
65 import java.util.regex.Matcher;
66 import java.util.regex.Pattern;
67
68 import org.forester.archaeopteryx.AptxConstants;
69 import org.forester.io.parsers.PhylogenyParser;
70 import org.forester.phylogeny.Phylogeny;
71 import org.forester.phylogeny.PhylogenyNode;
72 import org.forester.phylogeny.data.Distribution;
73 import org.forester.phylogeny.data.Sequence;
74 import org.forester.phylogeny.data.Taxonomy;
75 import org.forester.phylogeny.factories.ParserBasedPhylogenyFactory;
76 import org.forester.protein.BasicProtein;
77 import org.forester.protein.Domain;
78 import org.forester.protein.Protein;
79 import org.forester.sequence.MolecularSequence;
80 import org.forester.sequence.MolecularSequence.TYPE;
81 import org.forester.surfacing.SurfacingUtil;
82
83 public final class ForesterUtil {
84
85     public final static String       FILE_SEPARATOR                   = System.getProperty( "file.separator" );
86     public static final NumberFormat FORMATTER_06;
87     public static final NumberFormat FORMATTER_3;
88     public static final NumberFormat FORMATTER_6;
89     public static final NumberFormat FORMATTER_9;
90     public final static String       JAVA_VENDOR                      = System.getProperty( "java.vendor" );
91     public final static String       JAVA_VERSION                     = System.getProperty( "java.version" );
92     public final static String       LINE_SEPARATOR                   = System.getProperty( "line.separator" );
93     public static final String       NCBI_GI                          = "http://www.ncbi.nlm.nih.gov/protein/gi:";
94     public static final String       NCBI_NUCCORE                     = "http://www.ncbi.nlm.nih.gov/nuccore/";
95     public static final String       NCBI_PROTEIN                     = "http://www.ncbi.nlm.nih.gov/protein/";
96     public static final BigDecimal   NULL_BD                          = new BigDecimal( 0 );
97     public final static String       OS_ARCH                          = System.getProperty( "os.arch" );
98     public final static String       OS_NAME                          = System.getProperty( "os.name" );
99     public final static String       OS_VERSION                       = System.getProperty( "os.version" );
100     public static final String       PDB                              = "http://www.pdb.org/pdb/explore/explore.do?pdbId=";
101     public final static String       UNIPROT_KB                       = "http://www.uniprot.org/uniprot/";
102     public final static double       ZERO_DIFF                        = 1.0E-12;
103     private static final Pattern     PARANTHESESABLE_NH_CHARS_PATTERN = Pattern.compile( "[(),;\\s:\\[\\]]" );
104     static {
105         final DecimalFormatSymbols dfs = new DecimalFormatSymbols();
106         dfs.setDecimalSeparator( '.' );
107         // dfs.setGroupingSeparator( ( char ) 0 );
108         FORMATTER_9 = new DecimalFormat( "#.#########", dfs );
109         FORMATTER_6 = new DecimalFormat( "#.######", dfs );
110         FORMATTER_06 = new DecimalFormat( "0.######", dfs );
111         FORMATTER_3 = new DecimalFormat( "#.###", dfs );
112     }
113
114     final public static void appendSeparatorIfNotEmpty( final StringBuffer sb, final char separator ) {
115         if ( sb.length() > 0 ) {
116             sb.append( separator );
117         }
118     }
119
120     final public static String removeFileExtension( final String file_name ) {
121         if ( file_name.indexOf( "." ) > 0 ) {
122             return file_name.substring( 0, file_name.lastIndexOf( "." ) );
123         }
124         return file_name;
125     }
126
127     /**
128      * This calculates a color. If value is equal to min the returned color is
129      * minColor, if value is equal to max the returned color is maxColor,
130      * otherwise a color 'proportional' to value is returned.
131      *
132      * @param value
133      *            the value
134      * @param min
135      *            the smallest value
136      * @param max
137      *            the largest value
138      * @param minColor
139      *            the color for min
140      * @param maxColor
141      *            the color for max
142      * @return a Color
143      */
144     final public static Color calcColor( double value,
145                                          final double min,
146                                          final double max,
147                                          final Color minColor,
148                                          final Color maxColor ) {
149         if ( value < min ) {
150             value = min;
151         }
152         if ( value > max ) {
153             value = max;
154         }
155         final double x = ForesterUtil.calculateColorFactor( value, max, min );
156         final int red = ForesterUtil.calculateColorComponent( minColor.getRed(), maxColor.getRed(), x );
157         final int green = ForesterUtil.calculateColorComponent( minColor.getGreen(), maxColor.getGreen(), x );
158         final int blue = ForesterUtil.calculateColorComponent( minColor.getBlue(), maxColor.getBlue(), x );
159         return new Color( red, green, blue );
160     }
161
162     /**
163      * This calculates a color. If value is equal to min the returned color is
164      * minColor, if value is equal to max the returned color is maxColor, if
165      * value is equal to mean the returned color is meanColor, otherwise a color
166      * 'proportional' to value is returned -- either between min-mean or
167      * mean-max
168      *
169      * @param value
170      *            the value
171      * @param min
172      *            the smallest value
173      * @param max
174      *            the largest value
175      * @param mean
176      *            the mean/median value
177      * @param minColor
178      *            the color for min
179      * @param maxColor
180      *            the color for max
181      * @param meanColor
182      *            the color for mean
183      * @return a Color
184      */
185     final public static Color calcColor( double value,
186                                          final double min,
187                                          final double max,
188                                          final double mean,
189                                          final Color minColor,
190                                          final Color maxColor,
191                                          final Color meanColor ) {
192         if ( value < min ) {
193             value = min;
194         }
195         if ( value > max ) {
196             value = max;
197         }
198         if ( value < mean ) {
199             final double x = ForesterUtil.calculateColorFactor( value, mean, min );
200             final int red = ForesterUtil.calculateColorComponent( minColor.getRed(), meanColor.getRed(), x );
201             final int green = ForesterUtil.calculateColorComponent( minColor.getGreen(), meanColor.getGreen(), x );
202             final int blue = ForesterUtil.calculateColorComponent( minColor.getBlue(), meanColor.getBlue(), x );
203             return new Color( red, green, blue );
204         }
205         else if ( value > mean ) {
206             final double x = ForesterUtil.calculateColorFactor( value, max, mean );
207             final int red = ForesterUtil.calculateColorComponent( meanColor.getRed(), maxColor.getRed(), x );
208             final int green = ForesterUtil.calculateColorComponent( meanColor.getGreen(), maxColor.getGreen(), x );
209             final int blue = ForesterUtil.calculateColorComponent( meanColor.getBlue(), maxColor.getBlue(), x );
210             return new Color( red, green, blue );
211         }
212         else {
213             return meanColor;
214         }
215     }
216
217     /**
218      * Helper method for calcColor methods.
219      *
220      * @param smallercolor_component_x
221      *            color component the smaller color
222      * @param largercolor_component_x
223      *            color component the larger color
224      * @param x
225      *            factor
226      * @return an int representing a color component
227      */
228     final private static int calculateColorComponent( final double smallercolor_component_x,
229                                                       final double largercolor_component_x,
230                                                       final double x ) {
231         return ( int ) ( smallercolor_component_x
232                 + ( ( x * ( largercolor_component_x - smallercolor_component_x ) ) / 255.0 ) );
233     }
234
235     /**
236      * Helper method for calcColor methods.
237      *
238      *
239      * @param value
240      *            the value
241      * @param larger
242      *            the largest value
243      * @param smaller
244      *            the smallest value
245      * @return a normalized value between larger and smaller
246      */
247     final private static double calculateColorFactor( final double value, final double larger, final double smaller ) {
248         return ( 255.0 * ( value - smaller ) ) / ( larger - smaller );
249     }
250
251     public static int calculateOverlap( final Domain domain, final List<Boolean> covered_positions ) {
252         int overlap_count = 0;
253         for( int i = domain.getFrom(); i <= domain.getTo(); ++i ) {
254             if ( ( i < covered_positions.size() ) && ( covered_positions.get( i ) == true ) ) {
255                 ++overlap_count;
256             }
257         }
258         return overlap_count;
259     }
260
261     final public static String collapseWhiteSpace( final String s ) {
262         return s.replaceAll( "[\\s]+", " " );
263     }
264
265     final public static void collection2file( final File file, final Collection<?> data, final String separator )
266             throws IOException {
267         final Writer writer = new BufferedWriter( new FileWriter( file ) );
268         collection2writer( writer, data, separator );
269         writer.close();
270     }
271
272     final public static void collection2writer( final Writer writer, final Collection<?> data, final String separator )
273             throws IOException {
274         boolean first = true;
275         for( final Object object : data ) {
276             if ( !first ) {
277                 writer.write( separator );
278             }
279             else {
280                 first = false;
281             }
282             writer.write( object.toString() );
283         }
284     }
285
286     final public static String colorToHex( final Color color ) {
287         final String rgb = Integer.toHexString( color.getRGB() );
288         return rgb.substring( 2, rgb.length() );
289     }
290
291     synchronized public static void copyFile( final File in, final File out ) throws IOException {
292         final FileInputStream in_s = new FileInputStream( in );
293         final FileOutputStream out_s = new FileOutputStream( out );
294         try {
295             final byte[] buf = new byte[ 1024 ];
296             int i = 0;
297             while ( ( i = in_s.read( buf ) ) != -1 ) {
298                 out_s.write( buf, 0, i );
299             }
300         }
301         catch ( final IOException e ) {
302             throw e;
303         }
304         finally {
305             if ( in_s != null ) {
306                 in_s.close();
307             }
308             if ( out_s != null ) {
309                 out_s.close();
310             }
311         }
312     }
313
314     final public static int countChars( final String str, final char c ) {
315         int count = 0;
316         for( int i = 0; i < str.length(); ++i ) {
317             if ( str.charAt( i ) == c ) {
318                 ++count;
319             }
320         }
321         return count;
322     }
323
324     final public static BufferedWriter createBufferedWriter( final File file ) throws IOException {
325         if ( file.exists() ) {
326             throw new IOException( "[" + file + "] already exists" );
327         }
328         return new BufferedWriter( new FileWriter( file ) );
329     }
330
331     final public static BufferedWriter createBufferedWriter( final String name ) throws IOException {
332         return new BufferedWriter( new FileWriter( createFileForWriting( name ) ) );
333     }
334
335     final public static EasyWriter createEasyWriter( final File file ) throws IOException {
336         return new EasyWriter( createBufferedWriter( file ) );
337     }
338
339     final public static BufferedWriter createEasyWriter( final String name ) throws IOException {
340         return createEasyWriter( createFileForWriting( name ) );
341     }
342
343     final public static File createFileForWriting( final String name ) throws IOException {
344         final File file = new File( name );
345         if ( file.exists() ) {
346             throw new IOException( "[" + name + "] already exists" );
347         }
348         return file;
349     }
350
351     final public static void ensurePresenceOfDate( final PhylogenyNode node ) {
352         if ( !node.getNodeData().isHasDate() ) {
353             node.getNodeData().setDate( new org.forester.phylogeny.data.Date() );
354         }
355     }
356
357     final public static void ensurePresenceOfDistribution( final PhylogenyNode node ) {
358         if ( !node.getNodeData().isHasDistribution() ) {
359             node.getNodeData().setDistribution( new Distribution( "" ) );
360         }
361     }
362
363     public static void ensurePresenceOfSequence( final PhylogenyNode node ) {
364         if ( !node.getNodeData().isHasSequence() ) {
365             node.getNodeData().setSequence( new Sequence() );
366         }
367     }
368
369     public static void ensurePresenceOfTaxonomy( final PhylogenyNode node ) {
370         if ( !node.getNodeData().isHasTaxonomy() ) {
371             node.getNodeData().setTaxonomy( new Taxonomy() );
372         }
373     }
374
375     public static void fatalError( final String message ) {
376         System.err.println();
377         System.err.println( "error: " + message );
378         System.err.println();
379         System.exit( -1 );
380     }
381
382     public static void fatalError( final String prg_name, final String message ) {
383         System.err.println();
384         System.err.println( "[" + prg_name + "] > " + message );
385         System.err.println();
386         System.exit( -1 );
387     }
388
389     public static void fatalErrorIfFileNotReadable( final File file ) {
390         final String error = isReadableFile( file );
391         if ( !isEmpty( error ) ) {
392             System.err.println();
393             System.err.println( "error: " + error );
394             System.err.println();
395             System.exit( -1 );
396         }
397     }
398
399     public static void fatalErrorIfFileNotReadable( final String prg_name, final File file ) {
400         final String error = isReadableFile( file );
401         if ( !isEmpty( error ) ) {
402             System.err.println();
403             System.err.println( "[" + prg_name + "] > " + error );
404             System.err.println();
405             System.exit( -1 );
406         }
407     }
408
409     public static String[][] file22dArray( final File file ) throws IOException {
410         final List<String> list = new ArrayList<String>();
411         final BufferedReader in = new BufferedReader( new FileReader( file ) );
412         String str;
413         while ( ( str = in.readLine() ) != null ) {
414             str = str.trim();
415             if ( ( str.length() > 0 ) && !str.startsWith( "#" ) ) {
416                 list.add( str );
417             }
418         }
419         in.close();
420         final String[][] ary = new String[ list.size() ][ 2 ];
421         final Pattern pa = Pattern.compile( "(\\S+)\\s+(\\S+)" );
422         int i = 0;
423         for( final String s : list ) {
424             final Matcher m = pa.matcher( s );
425             if ( m.matches() ) {
426                 ary[ i ][ 0 ] = m.group( 1 );
427                 ary[ i ][ 1 ] = m.group( 2 );
428                 ++i;
429             }
430             else {
431                 throw new IOException( "unexpcted format: " + s );
432             }
433         }
434         return ary;
435     }
436
437     public static String[] file2array( final File file ) throws IOException {
438         final List<String> list = file2list( file );
439         final String[] ary = new String[ list.size() ];
440         int i = 0;
441         for( final String s : list ) {
442             ary[ i++ ] = s;
443         }
444         return ary;
445     }
446
447     final public static List<String> file2list( final File file ) throws IOException {
448         final List<String> list = new ArrayList<String>();
449         final BufferedReader in = new BufferedReader( new FileReader( file ) );
450         String str;
451         while ( ( str = in.readLine() ) != null ) {
452             str = str.trim();
453             if ( ( str.length() > 0 ) && !str.startsWith( "#" ) ) {
454                 for( final String s : splitString( str ) ) {
455                     list.add( s );
456                 }
457             }
458         }
459         in.close();
460         return list;
461     }
462
463     final public static SortedSet<String> file2set( final File file ) throws IOException {
464         final SortedSet<String> set = new TreeSet<String>();
465         final BufferedReader in = new BufferedReader( new FileReader( file ) );
466         String str;
467         while ( ( str = in.readLine() ) != null ) {
468             str = str.trim();
469             if ( ( str.length() > 0 ) && !str.startsWith( "#" ) ) {
470                 for( final String s : splitString( str ) ) {
471                     set.add( s );
472                 }
473             }
474         }
475         in.close();
476         return set;
477     }
478
479     final public static String getCurrentDateTime() {
480         final DateFormat format = new SimpleDateFormat( "yyyy/MM/dd HH:mm:ss" );
481         return format.format( new Date() );
482     }
483
484     final public static String getFileSeparator() {
485         return ForesterUtil.FILE_SEPARATOR;
486     }
487
488     final public static String getFirstLine( final Object source ) throws FileNotFoundException, IOException {
489         BufferedReader reader = null;
490         if ( source instanceof File ) {
491             final File f = ( File ) source;
492             if ( !f.exists() ) {
493                 throw new IOException( "[" + f.getAbsolutePath() + "] does not exist" );
494             }
495             else if ( !f.isFile() ) {
496                 throw new IOException( "[" + f.getAbsolutePath() + "] is not a file" );
497             }
498             else if ( !f.canRead() ) {
499                 throw new IOException( "[" + f.getAbsolutePath() + "] is not a readable" );
500             }
501             reader = new BufferedReader( new FileReader( f ) );
502         }
503         else if ( source instanceof InputStream ) {
504             reader = new BufferedReader( new InputStreamReader( ( InputStream ) source ) );
505         }
506         else if ( source instanceof String ) {
507             reader = new BufferedReader( new StringReader( ( String ) source ) );
508         }
509         else if ( source instanceof StringBuffer ) {
510             reader = new BufferedReader( new StringReader( source.toString() ) );
511         }
512         else if ( source instanceof URL ) {
513             final URLConnection url_connection = ( ( URL ) source ).openConnection();
514             url_connection.setDefaultUseCaches( false );
515             reader = new BufferedReader( new InputStreamReader( url_connection.getInputStream() ) );
516         }
517         else {
518             throw new IllegalArgumentException( "dont know how to read [" + source.getClass() + "]" );
519         }
520         String line;
521         while ( ( line = reader.readLine() ) != null ) {
522             line = line.trim();
523             if ( !ForesterUtil.isEmpty( line ) ) {
524                 if ( reader != null ) {
525                     reader.close();
526                 }
527                 return line;
528             }
529         }
530         if ( reader != null ) {
531             reader.close();
532         }
533         return line;
534     }
535
536     final public static String getForesterLibraryInformation() {
537         return "forester " + ForesterConstants.FORESTER_VERSION + " (" + ForesterConstants.FORESTER_DATE + ")";
538     }
539
540     final public static String getLineSeparator() {
541         return ForesterUtil.LINE_SEPARATOR;
542     }
543
544     final public static MolecularSequence.TYPE guessMolecularSequenceType( final String mol_seq ) {
545         final String s = mol_seq.toUpperCase();
546         if ( s.contains( "L" ) || s.contains( "I" ) || s.contains( "E" ) || s.contains( "H" ) || s.contains( "D" )
547                 || s.contains( "Q" ) ) {
548             return TYPE.AA;
549         }
550         else {
551             if ( s.contains( "T" ) ) {
552                 return TYPE.DNA;
553             }
554             else if ( s.contains( "U" ) ) {
555                 return TYPE.RNA;
556             }
557         }
558         return null;
559     }
560
561     final public static void increaseCountingMap( final Map<String, Integer> counting_map, final String item_name ) {
562         if ( !counting_map.containsKey( item_name ) ) {
563             counting_map.put( item_name, 1 );
564         }
565         else {
566             counting_map.put( item_name, counting_map.get( item_name ) + 1 );
567         }
568     }
569
570     final public static boolean isEmpty( final List<?> l ) {
571         if ( ( l == null ) || l.isEmpty() ) {
572             return true;
573         }
574         for( final Object o : l ) {
575             if ( o != null ) {
576                 return false;
577             }
578         }
579         return true;
580     }
581
582     final public static boolean isEmpty( final Set<?> s ) {
583         if ( ( s == null ) || s.isEmpty() ) {
584             return true;
585         }
586         for( final Object o : s ) {
587             if ( o != null ) {
588                 return false;
589             }
590         }
591         return true;
592     }
593
594     final public static boolean isEmpty( final String s ) {
595         return ( ( s == null ) || ( s.length() < 1 ) );
596     }
597
598     final public static boolean isEmptyTrimmed( final String s ) {
599         if ( s == null ) {
600             return true;
601         }
602         return ( ( s.trim().length() < 1 ) );
603     }
604
605     /**
606      * Returns true is Domain domain falls in an uninterrupted stretch of
607      * covered positions.
608      *
609      * @param domain
610      * @param covered_positions
611      * @return
612      */
613     public static boolean isEngulfed( final Domain domain, final List<Boolean> covered_positions ) {
614         for( int i = domain.getFrom(); i <= domain.getTo(); ++i ) {
615             if ( ( i >= covered_positions.size() ) || ( covered_positions.get( i ) != true ) ) {
616                 return false;
617             }
618         }
619         return true;
620     }
621
622     final public static boolean isEqual( final double a, final double b ) {
623         return ( ( Math.abs( a - b ) ) < ZERO_DIFF );
624     }
625
626     final public static boolean isEqual( final double a, final double b, final double tolerance ) {
627         return ( ( Math.abs( a - b ) ) < tolerance );
628     }
629
630     final public static boolean isEven( final int n ) {
631         return ( n % 2 ) == 0;
632     }
633
634     /**
635      * This determines whether String[] a and String[] b have at least one
636      * String in common (intersect). Returns false if at least one String[] is
637      * null or empty.
638      *
639      * @param a
640      *            a String[] b a String[]
641      * @return true if both a and b or not empty or null and contain at least
642      *         one element in common false otherwise
643      */
644     final public static boolean isIntersecting( final String[] a, final String[] b ) {
645         if ( ( a == null ) || ( b == null ) ) {
646             return false;
647         }
648         if ( ( a.length < 1 ) || ( b.length < 1 ) ) {
649             return false;
650         }
651         for( final String ai : a ) {
652             for( final String element : b ) {
653                 if ( ( ai != null ) && ( element != null ) && ai.equals( element ) ) {
654                     return true;
655                 }
656             }
657         }
658         return false;
659     }
660
661     final public static double isLargerOrEqualToZero( final double d ) {
662         if ( d > 0.0 ) {
663             return d;
664         }
665         else {
666             return 0.0;
667         }
668     }
669
670     public final static boolean isMac() {
671         try {
672             return OS_NAME.toLowerCase().startsWith( "mac" );
673         }
674         catch ( final Exception e ) {
675             ForesterUtil.printWarningMessage( AptxConstants.PRG_NAME, "minor error: " + e );
676             return false;
677         }
678     }
679
680     final public static boolean isNull( final BigDecimal s ) {
681         return ( ( s == null ) || ( s.compareTo( NULL_BD ) == 0 ) );
682     }
683
684     final public static String isReadableFile( final File f ) {
685         if ( !f.exists() ) {
686             return "file [" + f + "] does not exist";
687         }
688         if ( f.isDirectory() ) {
689             return "[" + f + "] is a directory";
690         }
691         if ( !f.isFile() ) {
692             return "[" + f + "] is not a file";
693         }
694         if ( !f.canRead() ) {
695             return "file [" + f + "] is not readable";
696         }
697         if ( f.length() < 1 ) {
698             return "file [" + f + "] is empty";
699         }
700         return null;
701     }
702
703     final public static String isReadableFile( final String s ) {
704         return isReadableFile( new File( s ) );
705     }
706
707     public final static boolean isWindows() {
708         try {
709             return OS_NAME.toLowerCase().indexOf( "win" ) > -1;
710         }
711         catch ( final Exception e ) {
712             ForesterUtil.printWarningMessage( AptxConstants.PRG_NAME, "minor error: " + e );
713             return false;
714         }
715     }
716
717     final public static String isWritableFile( final File f ) {
718         if ( f.isDirectory() ) {
719             return "[" + f + "] is a directory";
720         }
721         if ( f.exists() ) {
722             return "[" + f + "] already exists";
723         }
724         return null;
725     }
726
727     final public static String isWritableFile( final String s ) {
728         return isWritableFile( new File( s ) );
729     }
730
731     /**
732      * Helper for method "stringToColor".
733      * <p>
734      * (Last modified: 12/20/03)
735      */
736     final public static int limitRangeForColor( int i ) {
737         if ( i > 255 ) {
738             i = 255;
739         }
740         else if ( i < 0 ) {
741             i = 0;
742         }
743         return i;
744     }
745
746     final public static SortedMap<Object, Integer> listToSortedCountsMap( final List<?> list ) {
747         final SortedMap<Object, Integer> map = new TreeMap<Object, Integer>();
748         for( final Object key : list ) {
749             if ( !map.containsKey( key ) ) {
750                 map.put( key, 1 );
751             }
752             else {
753                 map.put( key, map.get( key ) + 1 );
754             }
755         }
756         return map;
757     }
758
759     final public static void map2file( final File file,
760                                        final Map<?, ?> data,
761                                        final String entry_separator,
762                                        final String data_separator )
763             throws IOException {
764         final Writer writer = new BufferedWriter( new FileWriter( file ) );
765         map2writer( writer, data, entry_separator, data_separator );
766         writer.close();
767     }
768
769     final public static void map2writer( final Writer writer,
770                                          final Map<?, ?> data,
771                                          final String entry_separator,
772                                          final String data_separator )
773             throws IOException {
774         boolean first = true;
775         for( final Entry<?, ?> entry : data.entrySet() ) {
776             if ( !first ) {
777                 writer.write( data_separator );
778             }
779             else {
780                 first = false;
781             }
782             writer.write( entry.getKey().toString() );
783             writer.write( entry_separator );
784             writer.write( entry.getValue().toString() );
785         }
786     }
787
788     final public static StringBuffer mapToStringBuffer( final Map<Object, Object> map,
789                                                         final String key_value_separator ) {
790         final StringBuffer sb = new StringBuffer();
791         for( final Object key : map.keySet() ) {
792             sb.append( key.toString() );
793             sb.append( key_value_separator );
794             sb.append( map.get( key ).toString() );
795             sb.append( ForesterUtil.getLineSeparator() );
796         }
797         return sb;
798     }
799
800     final public static String normalizeString( final String s,
801                                                 final int length,
802                                                 final boolean left_pad,
803                                                 final char pad_char ) {
804         if ( s.length() > length ) {
805             return s.substring( 0, length );
806         }
807         else {
808             final StringBuffer pad = new StringBuffer( length - s.length() );
809             for( int i = 0; i < ( length - s.length() ); ++i ) {
810                 pad.append( pad_char );
811             }
812             if ( left_pad ) {
813                 return pad + s;
814             }
815             else {
816                 return s + pad;
817             }
818         }
819     }
820
821     public final static Color obtainColorDependingOnTaxonomyGroup( final String tax_group ) {
822         if ( !ForesterUtil.isEmpty( tax_group ) ) {
823             if ( tax_group.equals( TaxonomyGroups.DEUTEROSTOMIA ) ) {
824                 return TaxonomyColors.DEUTEROSTOMIA_COLOR;
825             }
826             else if ( tax_group.equals( TaxonomyGroups.PROTOSTOMIA ) ) {
827                 return TaxonomyColors.PROTOSTOMIA_COLOR;
828             }
829             else if ( tax_group.equals( TaxonomyGroups.CNIDARIA ) ) {
830                 return TaxonomyColors.CNIDARIA_COLOR;
831             }
832             else if ( tax_group.equals( TaxonomyGroups.PLACOZOA ) ) {
833                 return TaxonomyColors.PLACOZOA_COLOR;
834             }
835             else if ( tax_group.equals( TaxonomyGroups.CTENOPHORA ) ) {
836                 return TaxonomyColors.CTENOPHORA_COLOR;
837             }
838             else if ( tax_group.equals( TaxonomyGroups.PORIFERA ) ) {
839                 return TaxonomyColors.PORIFERA_COLOR;
840             }
841             else if ( tax_group.equals( TaxonomyGroups.CHOANOFLAGELLIDA ) ) {
842                 return TaxonomyColors.CHOANOFLAGELLIDA;
843             }
844             else if ( tax_group.equals( TaxonomyGroups.ICHTHYOPHONIDA_FILASTEREA ) ) {
845                 return TaxonomyColors.ICHTHYOSPOREA_AND_FILASTEREA;
846             }
847             else if ( tax_group.equals( TaxonomyGroups.DIKARYA ) ) {
848                 return TaxonomyColors.DIKARYA_COLOR;
849             }
850             else if ( tax_group.equalsIgnoreCase( TaxonomyGroups.FUNGI )
851                     || tax_group.equalsIgnoreCase( TaxonomyGroups.OTHER_FUNGI ) ) {
852                 return TaxonomyColors.OTHER_FUNGI_COLOR;
853             }
854             else if ( tax_group.equals( TaxonomyGroups.NUCLEARIIDAE_AND_FONTICULA_GROUP ) ) {
855                 return TaxonomyColors.NUCLEARIIDAE_AND_FONTICULA_GROUP_COLOR;
856             }
857             else if ( tax_group.equals( TaxonomyGroups.AMOEBOZOA ) ) {
858                 return TaxonomyColors.AMOEBOZOA_COLOR;
859             }
860             else if ( tax_group.equals( TaxonomyGroups.EMBRYOPHYTA ) ) {
861                 return TaxonomyColors.EMBRYOPHYTA_COLOR;
862             }
863             else if ( tax_group.equals( TaxonomyGroups.CHLOROPHYTA ) ) {
864                 return TaxonomyColors.CHLOROPHYTA_COLOR;
865             }
866             else if ( tax_group.equals( TaxonomyGroups.RHODOPHYTA ) ) {
867                 return TaxonomyColors.RHODOPHYTA_COLOR;
868             }
869             else if ( tax_group.equals( TaxonomyGroups.HACROBIA ) ) {
870                 return TaxonomyColors.HACROBIA_COLOR;
871             }
872             else if ( tax_group.equals( TaxonomyGroups.GLAUCOCYSTOPHYCEAE ) ) {
873                 return TaxonomyColors.GLAUCOPHYTA_COLOR;
874             }
875             else if ( tax_group.equals( TaxonomyGroups.STRAMENOPILES ) ) {
876                 return TaxonomyColors.STRAMENOPILES_COLOR;
877             }
878             else if ( tax_group.equals( TaxonomyGroups.ALVEOLATA ) ) {
879                 return TaxonomyColors.ALVEOLATA_COLOR;
880             }
881             else if ( tax_group.equals( TaxonomyGroups.RHIZARIA ) ) {
882                 return TaxonomyColors.RHIZARIA_COLOR;
883             }
884             else if ( tax_group.equals( TaxonomyGroups.EXCAVATA ) ) {
885                 return TaxonomyColors.EXCAVATA_COLOR;
886             }
887             else if ( tax_group.equals( TaxonomyGroups.APUSOZOA ) ) {
888                 return TaxonomyColors.APUSOZOA_COLOR;
889             }
890             else if ( tax_group.equals( TaxonomyGroups.ARCHAEA ) ) {
891                 return TaxonomyColors.ARCHAEA_COLOR;
892             }
893             else if ( tax_group.equals( TaxonomyGroups.BACTERIA ) ) {
894                 return TaxonomyColors.BACTERIA_COLOR;
895             }
896             else if ( tax_group.equals( TaxonomyGroups.VIRUSES ) ) {
897                 return TaxonomyColors.VIRUSES_COLOR;
898             }
899             else if ( tax_group.equals( TaxonomyGroups.ALPHAHERPESVIRINAE ) ) {
900                 return TaxonomyColors.ALPHAHERPESVIRINAE_COLOR;
901             }
902             else if ( tax_group.equals( TaxonomyGroups.BETAHERPESVIRINAE ) ) {
903                 return TaxonomyColors.BETAHERPESVIRINAE_COLOR;
904             }
905             else if ( tax_group.equals( TaxonomyGroups.GAMMAHERPESVIRINAE ) ) {
906                 return TaxonomyColors.GAMMAHERPESVIRINAE_COLOR;
907             }
908             else if ( tax_group.equals( TaxonomyGroups.OTHER ) ) {
909                 return TaxonomyColors.OTHER_COLOR;
910             }
911         }
912         return null;
913     }
914
915     public final static String obtainNormalizedTaxonomyGroup( final String tax ) {
916         if ( tax.equalsIgnoreCase( TaxonomyGroups.DEUTEROSTOMIA ) ) {
917             return TaxonomyGroups.DEUTEROSTOMIA;
918         }
919         else if ( tax.equalsIgnoreCase( TaxonomyGroups.PROTOSTOMIA ) ) {
920             return TaxonomyGroups.PROTOSTOMIA;
921         }
922         else if ( tax.equalsIgnoreCase( TaxonomyGroups.CNIDARIA ) ) {
923             return TaxonomyGroups.CNIDARIA;
924         }
925         else if ( tax.toLowerCase().startsWith( "trichoplax" ) || tax.equalsIgnoreCase( TaxonomyGroups.PLACOZOA ) ) {
926             return TaxonomyGroups.PLACOZOA;
927         }
928         else if ( tax.toLowerCase().startsWith( "mnemiopsis" ) || tax.equalsIgnoreCase( TaxonomyGroups.CTENOPHORA ) ) {
929             return TaxonomyGroups.CTENOPHORA;
930         }
931         else if ( tax.toLowerCase().startsWith( "amphimedon" ) || tax.equalsIgnoreCase( TaxonomyGroups.PORIFERA ) ) {
932             return TaxonomyGroups.PORIFERA;
933         }
934         else if ( tax.equalsIgnoreCase( "codonosigidae" ) || tax.equalsIgnoreCase( TaxonomyGroups.CHOANOFLAGELLIDA ) ) {
935             return TaxonomyGroups.CHOANOFLAGELLIDA;
936         }
937         else if ( tax.toLowerCase().startsWith( TaxonomyGroups.ICHTHYOPHONIDA_FILASTEREA )
938                 || tax.toLowerCase().startsWith( "ichthyophonida and filasterea" )
939                 || tax.toLowerCase().startsWith( "ichthyosporea & filasterea" )
940                 || tax.toLowerCase().startsWith( "ichthyosporea and filasterea" ) ) {
941             return TaxonomyGroups.ICHTHYOPHONIDA_FILASTEREA;
942         }
943         else if ( tax.equalsIgnoreCase( TaxonomyGroups.DIKARYA ) ) {
944             return TaxonomyGroups.DIKARYA;
945         }
946         else if ( tax.equalsIgnoreCase( TaxonomyGroups.FUNGI ) || tax.equalsIgnoreCase( TaxonomyGroups.OTHER_FUNGI ) ) {
947             return TaxonomyGroups.OTHER_FUNGI;
948         }
949         else if ( tax.toLowerCase().startsWith( "nucleariidae and fonticula" ) ) {
950             return TaxonomyGroups.NUCLEARIIDAE_AND_FONTICULA_GROUP;
951         }
952         else if ( tax.equalsIgnoreCase( TaxonomyGroups.AMOEBOZOA ) ) {
953             return TaxonomyGroups.AMOEBOZOA;
954         }
955         else if ( tax.equalsIgnoreCase( TaxonomyGroups.EMBRYOPHYTA ) ) {
956             return TaxonomyGroups.EMBRYOPHYTA;
957         }
958         else if ( tax.equalsIgnoreCase( TaxonomyGroups.CHLOROPHYTA ) ) {
959             return TaxonomyGroups.CHLOROPHYTA;
960         }
961         else if ( tax.equalsIgnoreCase( TaxonomyGroups.RHODOPHYTA ) ) {
962             return TaxonomyGroups.RHODOPHYTA;
963         }
964         else if ( tax.toLowerCase().startsWith( TaxonomyGroups.HACROBIA ) ) {
965             return TaxonomyGroups.HACROBIA;
966         }
967         else if ( tax.equalsIgnoreCase( TaxonomyGroups.GLAUCOCYSTOPHYCEAE ) || tax.equalsIgnoreCase( "glaucophyta" ) ) {
968             return TaxonomyGroups.GLAUCOCYSTOPHYCEAE;
969         }
970         else if ( tax.equalsIgnoreCase( TaxonomyGroups.STRAMENOPILES ) ) {
971             return TaxonomyGroups.STRAMENOPILES;
972         }
973         else if ( tax.equalsIgnoreCase( TaxonomyGroups.ALVEOLATA ) ) {
974             return TaxonomyGroups.ALVEOLATA;
975         }
976         else if ( tax.equalsIgnoreCase( TaxonomyGroups.RHIZARIA ) ) {
977             return TaxonomyGroups.RHIZARIA;
978         }
979         else if ( tax.equalsIgnoreCase( TaxonomyGroups.EXCAVATA ) ) {
980             return TaxonomyGroups.EXCAVATA;
981         }
982         else if ( tax.equalsIgnoreCase( TaxonomyGroups.APUSOZOA ) ) {
983             return TaxonomyGroups.APUSOZOA;
984         }
985         else if ( tax.equalsIgnoreCase( TaxonomyGroups.ARCHAEA ) ) {
986             return TaxonomyGroups.ARCHAEA;
987         }
988         else if ( tax.equalsIgnoreCase( TaxonomyGroups.BACTERIA ) ) {
989             return TaxonomyGroups.BACTERIA;
990         }
991         else if ( tax.equalsIgnoreCase( TaxonomyGroups.BACTERIA ) ) {
992             return TaxonomyGroups.BACTERIA;
993         }
994         else if ( tax.equalsIgnoreCase( TaxonomyGroups.VIRUSES ) ) {
995             return TaxonomyGroups.VIRUSES;
996         }
997         else if ( tax.equalsIgnoreCase( TaxonomyGroups.ALPHAHERPESVIRINAE ) ) {
998             return TaxonomyGroups.ALPHAHERPESVIRINAE;
999         }
1000         else if ( tax.equalsIgnoreCase( TaxonomyGroups.BETAHERPESVIRINAE ) ) {
1001             return TaxonomyGroups.BETAHERPESVIRINAE;
1002         }
1003         else if ( tax.equalsIgnoreCase( TaxonomyGroups.GAMMAHERPESVIRINAE ) ) {
1004             return TaxonomyGroups.GAMMAHERPESVIRINAE;
1005         }
1006         return null;
1007     }
1008
1009     final public static BufferedReader obtainReader( final Object source ) throws IOException, FileNotFoundException {
1010         BufferedReader reader = null;
1011         if ( source instanceof File ) {
1012             final File f = ( File ) source;
1013             if ( !f.exists() ) {
1014                 throw new IOException( "\"" + f.getAbsolutePath() + "\" does not exist" );
1015             }
1016             else if ( !f.isFile() ) {
1017                 throw new IOException( "\"" + f.getAbsolutePath() + "\" is not a file" );
1018             }
1019             else if ( !f.canRead() ) {
1020                 throw new IOException( "\"" + f.getAbsolutePath() + "\" is not a readable" );
1021             }
1022             reader = new BufferedReader( new FileReader( f ) );
1023         }
1024         else if ( source instanceof InputStream ) {
1025             reader = new BufferedReader( new InputStreamReader( ( InputStream ) source ) );
1026         }
1027         else if ( source instanceof String ) {
1028             reader = new BufferedReader( new StringReader( ( String ) source ) );
1029         }
1030         else if ( source instanceof StringBuffer ) {
1031             reader = new BufferedReader( new StringReader( source.toString() ) );
1032         }
1033         else {
1034             throw new IllegalArgumentException( "attempt to parse object of type [" + source.getClass()
1035                     + "] (can only parse objects of type File, InputStream, String, or StringBuffer)" );
1036         }
1037         return reader;
1038     }
1039
1040     public final static void outOfMemoryError( final OutOfMemoryError e ) {
1041         System.err.println();
1042         System.err.println( "Java memory allocation might be too small, try \"-Xmx2048m\" java command line option" );
1043         System.err.println();
1044         e.printStackTrace( System.err );
1045         System.err.println();
1046         System.exit( -1 );
1047     }
1048
1049     final public static StringBuffer pad( final double number,
1050                                           final int size,
1051                                           final char pad,
1052                                           final boolean left_pad ) {
1053         return pad( new StringBuffer( number + "" ), size, pad, left_pad );
1054     }
1055
1056     final public static StringBuffer pad( final String string,
1057                                           final int size,
1058                                           final char pad,
1059                                           final boolean left_pad ) {
1060         return pad( new StringBuffer( string ), size, pad, left_pad );
1061     }
1062
1063     final public static StringBuffer pad( final StringBuffer string,
1064                                           final int size,
1065                                           final char pad,
1066                                           final boolean left_pad ) {
1067         final StringBuffer padding = new StringBuffer();
1068         final int s = size - string.length();
1069         if ( s < 1 ) {
1070             return new StringBuffer( string.substring( 0, size ) );
1071         }
1072         for( int i = 0; i < s; ++i ) {
1073             padding.append( pad );
1074         }
1075         if ( left_pad ) {
1076             return padding.append( string );
1077         }
1078         else {
1079             return string.append( padding );
1080         }
1081     }
1082
1083     final public static double parseDouble( final String str ) throws ParseException {
1084         if ( ForesterUtil.isEmpty( str ) ) {
1085             return 0.0;
1086         }
1087         return Double.parseDouble( str );
1088     }
1089
1090     final public static int parseInt( final String str ) throws ParseException {
1091         if ( ForesterUtil.isEmpty( str ) ) {
1092             return 0;
1093         }
1094         return Integer.parseInt( str );
1095     }
1096
1097     final public static void printArray( final Object[] a ) {
1098         for( int i = 0; i < a.length; ++i ) {
1099             System.out.println( "[" + i + "]=" + a[ i ] );
1100         }
1101     }
1102
1103     final public static void printCountingMap( final Map<String, Integer> counting_map ) {
1104         for( final String key : counting_map.keySet() ) {
1105             System.out.println( key + ": " + counting_map.get( key ) );
1106         }
1107     }
1108
1109     final public static void printErrorMessage( final String prg_name, final String message ) {
1110         System.err.println( "[" + prg_name + "] > error: " + message );
1111     }
1112
1113     final public static void printProgramInformation( final String prg_name,
1114                                                       final String prg_version,
1115                                                       final String date ) {
1116         final int l = prg_name.length() + prg_version.length() + date.length() + 4;
1117         System.out.println();
1118         System.out.println( prg_name + " " + prg_version + " (" + date + ")" );
1119         for( int i = 0; i < l; ++i ) {
1120             System.out.print( "_" );
1121         }
1122         System.out.println();
1123     }
1124
1125     final public static void printProgramInformation( final String prg_name,
1126                                                       final String prg_version,
1127                                                       final String date,
1128                                                       final String email,
1129                                                       final String www ) {
1130         printProgramInformation( prg_name, null, prg_version, date, email, www, null );
1131     }
1132
1133     final public static void printProgramInformation( final String prg_name,
1134                                                       final String desc,
1135                                                       final String prg_version,
1136                                                       final String date,
1137                                                       final String email,
1138                                                       final String www,
1139                                                       final String based_on ) {
1140         String my_prg_name = new String( prg_name );
1141         if ( !ForesterUtil.isEmpty( desc ) ) {
1142             my_prg_name += ( " - " + desc );
1143         }
1144         final int l = my_prg_name.length() + prg_version.length() + date.length() + 4;
1145         System.out.println();
1146         System.out.println( my_prg_name + " " + prg_version + " (" + date + ")" );
1147         for( int i = 0; i < l; ++i ) {
1148             System.out.print( "_" );
1149         }
1150         System.out.println();
1151         System.out.println();
1152         System.out.println( "WWW     : " + www );
1153         System.out.println( "Contact : " + email );
1154         if ( !ForesterUtil.isEmpty( based_on ) ) {
1155             System.out.println( "Based on: " + based_on );
1156         }
1157         if ( !ForesterUtil.isEmpty( ForesterUtil.JAVA_VERSION ) && !ForesterUtil.isEmpty( ForesterUtil.JAVA_VENDOR ) ) {
1158             System.out.println();
1159             System.out
1160                     .println( "[running on Java " + ForesterUtil.JAVA_VERSION + " " + ForesterUtil.JAVA_VENDOR + "]" );
1161         }
1162         System.out.println();
1163     }
1164
1165     final public static void printWarningMessage( final String prg_name, final String message ) {
1166         System.out.println( "[" + prg_name + "] > warning: " + message );
1167     }
1168
1169     final public static void programMessage( final String prg_name, final String message ) {
1170         System.out.println( "[" + prg_name + "] > " + message );
1171     }
1172
1173     public static List<String> readUrl( final String url_str ) throws IOException {
1174         final URL url = new URL( url_str );
1175         final URLConnection urlc = url.openConnection();
1176         //urlc.setRequestProperty( "User-Agent", "" );
1177         final BufferedReader in = new BufferedReader( new InputStreamReader( urlc.getInputStream() ) );
1178         String line;
1179         final List<String> result = new ArrayList<String>();
1180         while ( ( line = in.readLine() ) != null ) {
1181             result.add( line );
1182         }
1183         in.close();
1184         return result;
1185     }
1186
1187     /**
1188      *
1189      * Example regarding engulfment: ------------0.1 ----------0.2 --0.3 =>
1190      * domain with 0.3 is ignored
1191      *
1192      * -----------0.1 ----------0.2 --0.3 => domain with 0.3 is ignored
1193      *
1194      *
1195      * ------------0.1 ----------0.3 --0.2 => domains with 0.3 and 0.2 are _not_
1196      * ignored
1197      *
1198      * @param max_allowed_overlap
1199      *            maximal allowed overlap (inclusive) to be still considered not
1200      *            overlapping (zero or negative value to allow any overlap)
1201      * @param remove_engulfed_domains
1202      *            to remove domains which are completely engulfed by coverage of
1203      *            domains with better support
1204      * @param protein
1205      * @return
1206      */
1207     public static Protein removeOverlappingDomains( final int max_allowed_overlap,
1208                                                     final boolean remove_engulfed_domains,
1209                                                     final Protein protein ) {
1210         final Protein pruned_protein = new BasicProtein( protein.getProteinId().getId(),
1211                                                          protein.getSpecies().getSpeciesId(),
1212                                                          protein.getLength() );
1213         final List<Domain> sorted = SurfacingUtil.sortDomainsWithAscendingConfidenceValues( protein );
1214         final List<Boolean> covered_positions = new ArrayList<Boolean>();
1215         for( final Domain domain : sorted ) {
1216             if ( ( ( max_allowed_overlap < 0 )
1217                     || ( ForesterUtil.calculateOverlap( domain, covered_positions ) <= max_allowed_overlap ) )
1218                     && ( !remove_engulfed_domains || !isEngulfed( domain, covered_positions ) ) ) {
1219                 final int covered_positions_size = covered_positions.size();
1220                 for( int i = covered_positions_size; i < domain.getFrom(); ++i ) {
1221                     covered_positions.add( false );
1222                 }
1223                 final int new_covered_positions_size = covered_positions.size();
1224                 for( int i = domain.getFrom(); i <= domain.getTo(); ++i ) {
1225                     if ( i < new_covered_positions_size ) {
1226                         covered_positions.set( i, true );
1227                     }
1228                     else {
1229                         covered_positions.add( true );
1230                     }
1231                 }
1232                 pruned_protein.addProteinDomain( domain );
1233             }
1234         }
1235         return pruned_protein;
1236     }
1237
1238     final public static String removeSuffix( final String file_name ) {
1239         final int i = file_name.lastIndexOf( '.' );
1240         if ( i > 1 ) {
1241             return file_name.substring( 0, i );
1242         }
1243         return file_name;
1244     }
1245
1246     /**
1247      * Removes all white space from String s.
1248      *
1249      * @return String s with white space removed
1250      */
1251     final public static String removeWhiteSpace( String s ) {
1252         int i;
1253         for( i = 0; i <= ( s.length() - 1 ); i++ ) {
1254             if ( ( s.charAt( i ) == ' ' ) || ( s.charAt( i ) == '\t' ) || ( s.charAt( i ) == '\n' )
1255                     || ( s.charAt( i ) == '\r' ) ) {
1256                 s = s.substring( 0, i ) + s.substring( i + 1 );
1257                 i--;
1258             }
1259         }
1260         return s;
1261     }
1262
1263     final public static String replaceIllegalNhxCharacters( final String nhx ) {
1264         if ( nhx == null ) {
1265             return "";
1266         }
1267         return nhx.trim().replaceAll( "[\\[\\]']+", "_" );
1268     }
1269
1270     final public static double round( final double value, final int decimal_place ) {
1271         BigDecimal bd = new BigDecimal( value );
1272         bd = bd.setScale( decimal_place, BigDecimal.ROUND_HALF_UP );
1273         return bd.doubleValue();
1274     }
1275
1276     /**
1277      * Rounds d to an int.
1278      */
1279     final public static int roundToInt( final double d ) {
1280         return ( int ) ( d + 0.5 );
1281     }
1282
1283     final public static int roundToInt( final float f ) {
1284         return ( int ) ( f + 0.5f );
1285     }
1286
1287     final public static short roundToShort( final double d ) {
1288         return ( short ) ( d + 0.5 );
1289     }
1290
1291     final public static String sanitizeString( final String s ) {
1292         if ( s == null ) {
1293             return "";
1294         }
1295         else {
1296             return s.trim();
1297         }
1298     }
1299
1300     public final static StringBuilder santitizeStringForNH( String data ) {
1301         data = data.replaceAll( "\\s+", " " ).trim();
1302         final StringBuilder sb = new StringBuilder();
1303         if ( data.length() > 0 ) {
1304             final boolean single_pars = data.indexOf( '\'' ) > -1;
1305             final boolean double_pars = data.indexOf( '"' ) > -1;
1306             if ( single_pars && double_pars ) {
1307                 data = data.replace( '\'', '`' );
1308                 sb.append( '\'' );
1309                 sb.append( data );
1310                 sb.append( '\'' );
1311             }
1312             else if ( single_pars ) {
1313                 sb.append( '"' );
1314                 sb.append( data );
1315                 sb.append( '"' );
1316             }
1317             else if ( PARANTHESESABLE_NH_CHARS_PATTERN.matcher( data ).find() ) {
1318                 sb.append( '\'' );
1319                 sb.append( data );
1320                 sb.append( '\'' );
1321             }
1322             else {
1323                 sb.append( data );
1324             }
1325         }
1326         return sb;
1327     }
1328
1329     public static boolean seqIsLikelyToBeAa( final String s ) {
1330         final String seq = s.toLowerCase();
1331         if ( ( seq.indexOf( 'r' ) > -1 ) || ( seq.indexOf( 'd' ) > -1 ) || ( seq.indexOf( 'e' ) > -1 )
1332                 || ( seq.indexOf( 'q' ) > -1 ) || ( seq.indexOf( 'h' ) > -1 ) || ( seq.indexOf( 'k' ) > -1 )
1333                 || ( seq.indexOf( 'w' ) > -1 ) || ( seq.indexOf( 's' ) > -1 ) || ( seq.indexOf( 'm' ) > -1 )
1334                 || ( seq.indexOf( 'p' ) > -1 ) || ( seq.indexOf( 'v' ) > -1 ) ) {
1335             return true;
1336         }
1337         return false;
1338     }
1339
1340     final private static String[] splitString( final String str ) {
1341         final String regex = "[\\s;,]+";
1342         return str.split( regex );
1343     }
1344
1345     final public static String stringArrayToString( final String[] a ) {
1346         return stringArrayToString( a, ", " );
1347     }
1348
1349     final public static String stringArrayToString( final String[] a, final String separator ) {
1350         final StringBuilder sb = new StringBuilder();
1351         if ( ( a != null ) && ( a.length > 0 ) ) {
1352             for( int i = 0; i < ( a.length - 1 ); ++i ) {
1353                 sb.append( a[ i ] + separator );
1354             }
1355             sb.append( a[ a.length - 1 ] );
1356         }
1357         return sb.toString();
1358     }
1359
1360     final public static String[] stringListToArray( final List<String> list ) {
1361         if ( list != null ) {
1362             final String[] str = new String[ list.size() ];
1363             int i = 0;
1364             for( final String l : list ) {
1365                 str[ i++ ] = l;
1366             }
1367             return str;
1368         }
1369         return null;
1370     }
1371
1372     final public static String stringListToString( final List<String> l, final String separator ) {
1373         final StringBuilder sb = new StringBuilder();
1374         if ( ( l != null ) && ( l.size() > 0 ) ) {
1375             for( int i = 0; i < ( l.size() - 1 ); ++i ) {
1376                 sb.append( l.get( i ) + separator );
1377             }
1378             sb.append( l.get( l.size() - 1 ) );
1379         }
1380         return sb.toString();
1381     }
1382
1383     final public static String[] stringSetToArray( final Set<String> strings ) {
1384         final String[] str_array = new String[ strings.size() ];
1385         int i = 0;
1386         for( final String e : strings ) {
1387             str_array[ i++ ] = e;
1388         }
1389         return str_array;
1390     }
1391
1392     final public static void unexpectedFatalError( final Error e ) {
1393         System.err.println();
1394         System.err.println( "unexpected error: should not have occured! Please contact program author(s)." );
1395         e.printStackTrace( System.err );
1396         System.err.println();
1397         System.exit( -1 );
1398     }
1399
1400     final public static void unexpectedFatalError( final Exception e ) {
1401         System.err.println();
1402         System.err.println( "unexpected exception: should not have occured! Please contact program author(s)." );
1403         e.printStackTrace( System.err );
1404         System.err.println();
1405         System.exit( -1 );
1406     }
1407
1408     final public static void unexpectedFatalError( final String message ) {
1409         System.err.println();
1410         System.err.println( "unexpected error: should not have occured! Please contact program author(s)." );
1411         System.err.println( message );
1412         System.err.println();
1413         System.exit( -1 );
1414     }
1415
1416     final public static void unexpectedFatalError( final String prg_name, final Exception e ) {
1417         System.err.println();
1418         System.err.println( "[" + prg_name
1419                 + "] > unexpected error; should not have occured! Please contact program author(s)." );
1420         e.printStackTrace( System.err );
1421         System.err.println();
1422         System.exit( -1 );
1423     }
1424
1425     final public static void unexpectedFatalError( final String prg_name, final String message ) {
1426         System.err.println();
1427         System.err.println( "[" + prg_name
1428                 + "] > unexpected error: should not have occured! Please contact program author(s)." );
1429         System.err.println( message );
1430         System.err.println();
1431         System.exit( -1 );
1432     }
1433
1434     final public static void unexpectedFatalError( final String prg_name, final String message, final Exception e ) {
1435         System.err.println();
1436         System.err.println( "[" + prg_name
1437                 + "] > unexpected error: should not have occured! Please contact program author(s)." );
1438         System.err.println( message );
1439         e.printStackTrace( System.err );
1440         System.err.println();
1441         System.exit( -1 );
1442     }
1443
1444     public final static void updateProgress( final double progress_percentage ) {
1445         final int width = 50;
1446         System.out.print( "\r[" );
1447         int i = 0;
1448         for( ; i <= ForesterUtil.roundToInt( progress_percentage * width ); i++ ) {
1449             System.out.print( "." );
1450         }
1451         for( ; i < width; i++ ) {
1452             System.out.print( " " );
1453         }
1454         System.out.print( "]" );
1455     }
1456
1457     public final static void updateProgress( final int i, final DecimalFormat f ) {
1458         System.out.print( "\r[" + f.format( i ) + "]" );
1459     }
1460
1461     public final static String wordWrap( final String str, final int width ) {
1462         final StringBuilder sb = new StringBuilder( str );
1463         int start = 0;
1464         int ls = -1;
1465         int i = 0;
1466         while ( i < sb.length() ) {
1467             if ( sb.charAt( i ) == ' ' ) {
1468                 ls = i;
1469             }
1470             if ( sb.charAt( i ) == '\n' ) {
1471                 ls = -1;
1472                 start = i + 1;
1473             }
1474             if ( i > ( ( start + width ) - 1 ) ) {
1475                 if ( ls != -1 ) {
1476                     sb.setCharAt( ls, '\n' );
1477                     start = ls + 1;
1478                     ls = -1;
1479                 }
1480                 else {
1481                     sb.insert( i, '\n' );
1482                     start = i + 1;
1483                 }
1484             }
1485             i++;
1486         }
1487         return sb.toString();
1488     }
1489
1490     public final static Phylogeny[] readPhylogeniesFromUrl( final URL url, final PhylogenyParser parser )
1491             throws NoSuchAlgorithmException, IOException, KeyManagementException {
1492         if ( url == null ) {
1493             throw new IllegalArgumentException( "URL to read from must not be null" );
1494         }
1495         else if ( parser == null ) {
1496             throw new IllegalArgumentException( "parser to use to read from URL must not be null" );
1497         }
1498         final URLConnection con;
1499         if ( url.toString().startsWith( "https:" ) ) {
1500             con = TrustManager.makeHttpsURLConnection( url );
1501         }
1502         else if ( url.toString().startsWith( "http:" ) ) {
1503             con = url.openConnection();
1504         }
1505         else {
1506             throw new IllegalArgumentException( "Cannot deal with URL: " + url );
1507         }
1508         if ( con == null ) {
1509             throw new IOException( "could not create connection from " + url );
1510         }
1511         con.setDefaultUseCaches( false );
1512         final InputStream is = con.getInputStream();
1513         if ( is == null ) {
1514             throw new IOException( "could not create input stream from " + url );
1515         }
1516         final Phylogeny[] trees = ParserBasedPhylogenyFactory.getInstance().create( is, parser );
1517         try {
1518             is.close();
1519         }
1520         catch ( final Exception e ) {
1521             // ignore
1522         }
1523         return trees;
1524     }
1525
1526     public final static File getMatchingFile( final File dir, final String prefix, final String suffix )
1527             throws IOException {
1528         if ( !dir.exists() ) {
1529             throw new IOException( "[" + dir + "] does not exist" );
1530         }
1531         if ( !dir.isDirectory() ) {
1532             throw new IOException( "[" + dir + "] is not a directory" );
1533         }
1534         if ( dir.listFiles().length == 0 ) {
1535             throw new IOException( "[" + dir + "] is empty" );
1536         }
1537         final File files[] = dir.listFiles( new FilenameFilter() {
1538
1539             @Override
1540             public boolean accept( final File dir, final String name ) {
1541                 return ( name.endsWith( suffix ) );
1542             }
1543         } );
1544         if ( files.length == 0 ) {
1545             throw new IOException( "no files ending with \"" + suffix + "\" found in [" + dir + "]" );
1546         }
1547         String my_prefix = prefix;
1548         boolean done = false;
1549         boolean more_than_one = false;
1550         File the_one = null;
1551         do {
1552             int matches = 0;
1553             for( final File file : files ) {
1554                 if ( file.getName().startsWith( my_prefix ) ) {
1555                     matches++;
1556                     if ( matches > 1 ) {
1557                         the_one = null;
1558                         break;
1559                     }
1560                     the_one = file;
1561                 }
1562             }
1563             if ( matches > 1 ) {
1564                 more_than_one = true;
1565                 done = true;
1566             }
1567             if ( matches == 1 ) {
1568                 done = true;
1569             }
1570             else {
1571                 if ( my_prefix.length() <= 1 ) {
1572                     throw new IOException( "no file matching \"" + removeFileExtension( prefix )
1573                             + "\" and ending with \"" + suffix + "\" found in [" + dir + "]" );
1574                 }
1575                 my_prefix = my_prefix.substring( 0, my_prefix.length() - 1 );
1576             }
1577         } while ( !done );
1578         if ( more_than_one ) {
1579             throw new IOException( "multiple files matching \"" + removeFileExtension( prefix )
1580                     + "\" and ending with \"" + suffix + "\" found in [" + dir + "]" );
1581         }
1582         else if ( the_one != null ) {
1583         }
1584         else {
1585             throw new IOException( "no file matching \"" + removeFileExtension( prefix ) + "\" and ending with \""
1586                     + suffix + "\" found in [" + dir + "]" );
1587         }
1588         return the_one;
1589     }
1590
1591     public final static String greatestCommonPrefix( final String a, final String b ) {
1592         final int min_length = Math.min( a.length(), b.length() );
1593         for( int i = 0; i < min_length; ++i ) {
1594             if ( a.charAt( i ) != b.charAt( i ) ) {
1595                 return a.substring( 0, i );
1596             }
1597         }
1598         return a.substring( 0, min_length );
1599     }
1600
1601     public final static boolean isContainsPrefix( final String word, final String prefix, final String separator ) {
1602         if ( ForesterUtil.isEmpty( separator ) ) {
1603             throw new IllegalArgumentException( "separator must not be null or empty" );
1604         }
1605         final String[] word_ary = word.split( Pattern.quote( separator ) );
1606         final String[] prefix_ary = prefix.split( Pattern.quote( separator ) );
1607         if ( word_ary.length < prefix_ary.length ) {
1608             return false;
1609         }
1610         final int prefix_ary_length = prefix_ary.length;
1611         for( int i = 0; i < prefix_ary_length; ++i ) {
1612             if ( !( word_ary[ i ].equals( prefix_ary[ i ] ) ) ) {
1613                 return false;
1614             }
1615         }
1616         return true;
1617     }
1618
1619     public final static String greatestCommonPrefix( final String a, final String b, final String separator ) {
1620         if ( ForesterUtil.isEmpty( separator ) ) {
1621             throw new IllegalArgumentException( "separator must not be null or empty" );
1622         }
1623         final String[] as = a.split( Pattern.quote( separator ) );
1624         final String[] bs = b.split( Pattern.quote( separator ) );
1625         final int min_length = Math.min( as.length, bs.length );
1626         for( int i = 0; i < min_length; ++i ) {
1627             if ( !( as[ i ].equals( bs[ i ] ) ) ) {
1628                 final StringBuilder sb = new StringBuilder();
1629                 boolean first = true;
1630                 for( int j = 0; j < i; ++j ) {
1631                     if ( first ) {
1632                         first = false;
1633                     }
1634                     else {
1635                         sb.append( separator );
1636                     }
1637                     sb.append( as[ j ] );
1638                 }
1639                 return sb.toString();
1640             }
1641         }
1642         final StringBuilder sb = new StringBuilder();
1643         boolean first = true;
1644         for( int j = 0; j < min_length; ++j ) {
1645             if ( first ) {
1646                 first = false;
1647             }
1648             else {
1649                 sb.append( separator );
1650             }
1651             sb.append( as[ j ] );
1652         }
1653         return sb.toString();
1654     }
1655
1656     public final static String greatestCommonPrefix( final List<String> strings ) {
1657         if ( strings == null ) {
1658             throw new IllegalArgumentException( "list of strings is null" );
1659         }
1660         if ( strings.isEmpty() ) {
1661             throw new IllegalArgumentException( "list of strings is empty" );
1662         }
1663         String common = strings.get( 0 );
1664         for( int i = 1; i < strings.size(); ++i ) {
1665             common = greatestCommonPrefix( common, strings.get( i ) );
1666         }
1667         return common;
1668     }
1669
1670     public final static String greatestCommonPrefix( final List<String> strings, final String separator ) {
1671         if ( ForesterUtil.isEmpty( separator ) ) {
1672             return greatestCommonPrefix( strings );
1673         }
1674         if ( strings == null ) {
1675             throw new IllegalArgumentException( "list of strings is null" );
1676         }
1677         if ( strings.isEmpty() ) {
1678             throw new IllegalArgumentException( "list of strings is empty" );
1679         }
1680         String common = strings.get( 0 );
1681         for( int i = 1; i < strings.size(); ++i ) {
1682             common = greatestCommonPrefix( common, strings.get( i ), separator );
1683         }
1684         return common;
1685     }
1686
1687     public static List<String> spliIntoPrefixes( final String prefix, final String separator ) {
1688         final String[] a = prefix.split( Pattern.quote( separator ) );
1689         final List<String> l = new ArrayList<String>();
1690         for( int i = 0; i < a.length; ++i ) {
1691             final StringBuilder sb = new StringBuilder();
1692             for( int j = 0; j <= i; ++j ) {
1693                 sb.append( a[ j ] );
1694                 if ( j < i ) {
1695                     sb.append( separator );
1696                 }
1697             }
1698             l.add( sb.toString() );
1699         }
1700         return l;
1701     }
1702
1703     public static boolean isLooksLikeFasta( final File file ) throws IOException {
1704         final String first_line = ForesterUtil.getFirstLine( file ).trim().toLowerCase();
1705         return ( ( !isEmptyTrimmed( first_line ) && first_line.trim().startsWith( ">" ) ) );
1706     }
1707
1708     private ForesterUtil() {
1709     }
1710 }