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