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