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