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