64f3f81ccc0467b431025aebc718a06402cd44a0
[jalview.git] / 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: www.phylosoft.org/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.math.BigDecimal;
42 import java.net.URL;
43 import java.text.DateFormat;
44 import java.text.DecimalFormat;
45 import java.text.DecimalFormatSymbols;
46 import java.text.NumberFormat;
47 import java.text.ParseException;
48 import java.text.SimpleDateFormat;
49 import java.util.ArrayList;
50 import java.util.Date;
51 import java.util.Hashtable;
52 import java.util.Iterator;
53 import java.util.List;
54 import java.util.Map;
55 import java.util.Set;
56 import java.util.SortedMap;
57 import java.util.SortedSet;
58 import java.util.TreeMap;
59 import java.util.TreeSet;
60 import java.util.regex.Matcher;
61 import java.util.regex.Pattern;
62
63 import org.forester.io.parsers.PhylogenyParser;
64 import org.forester.io.parsers.nexus.NexusPhylogeniesParser;
65 import org.forester.io.parsers.nhx.NHXParser;
66 import org.forester.io.parsers.phyloxml.PhyloXmlParser;
67 import org.forester.io.parsers.phyloxml.PhyloXmlUtil;
68 import org.forester.io.parsers.tol.TolParser;
69 import org.forester.io.parsers.util.PhylogenyParserException;
70 import org.forester.phylogeny.Phylogeny;
71 import org.forester.phylogeny.PhylogenyMethods;
72 import org.forester.phylogeny.PhylogenyNode;
73 import org.forester.phylogeny.data.Confidence;
74 import org.forester.phylogeny.data.Distribution;
75 import org.forester.phylogeny.data.Identifier;
76 import org.forester.phylogeny.data.Sequence;
77 import org.forester.phylogeny.data.Taxonomy;
78 import org.forester.phylogeny.factories.ParserBasedPhylogenyFactory;
79 import org.forester.phylogeny.factories.PhylogenyFactory;
80 import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
81
82 public final class ForesterUtil {
83
84     public final static String       FILE_SEPARATOR                   = System.getProperty( "file.separator" );
85     public final static String       LINE_SEPARATOR                   = System.getProperty( "line.separator" );
86     public final static String       JAVA_VENDOR                      = System.getProperty( "java.vendor" );
87     public final static String       JAVA_VERSION                     = System.getProperty( "java.version" );
88     public final static String       OS_ARCH                          = System.getProperty( "os.arch" );
89     public final static String       OS_NAME                          = System.getProperty( "os.name" );
90     public final static String       OS_VERSION                       = System.getProperty( "os.version" );
91     public final static Pattern      PARANTHESESABLE_NH_CHARS_PATTERN = Pattern.compile( "[(),;\\s]" );
92     public final static double       ZERO_DIFF                        = 1.0E-9;
93     public static final BigDecimal   NULL_BD                          = new BigDecimal( 0 );
94     public static final NumberFormat FORMATTER_9;
95     public static final NumberFormat FORMATTER_6;
96     public static final NumberFormat FORMATTER_06;
97     public static final NumberFormat FORMATTER_3;
98     static {
99         final DecimalFormatSymbols dfs = new DecimalFormatSymbols();
100         dfs.setDecimalSeparator( '.' );
101         // dfs.setGroupingSeparator( ( char ) 0 );
102         FORMATTER_9 = new DecimalFormat( "#.#########", dfs );
103         FORMATTER_6 = new DecimalFormat( "#.######", dfs );
104         FORMATTER_06 = new DecimalFormat( "0.######", dfs );
105         FORMATTER_3 = new DecimalFormat( "#.###", dfs );
106     }
107
108     private ForesterUtil() {
109     }
110
111     final public static void appendSeparatorIfNotEmpty( final StringBuffer sb, final char separator ) {
112         if ( sb.length() > 0 ) {
113             sb.append( separator );
114         }
115     }
116
117     /**
118      * This calculates a color. If value is equal to min the returned color is
119      * minColor, if value is equal to max the returned color is maxColor,
120      * otherwise a color 'proportional' to value is returned.
121      * 
122      * @param value
123      *            the value 
124      * @param min
125      *            the smallest value 
126      * @param max
127      *            the largest value 
128      * @param minColor
129      *            the color for min
130      * @param maxColor
131      *            the color for max
132      * @return a Color
133      */
134     final public static Color calcColor( double value,
135                                          final double min,
136                                          final double max,
137                                          final Color minColor,
138                                          final Color maxColor ) {
139         if ( value < min ) {
140             value = min;
141         }
142         if ( value > max ) {
143             value = max;
144         }
145         final double x = ForesterUtil.calculateColorFactor( value, max, min );
146         final int red = ForesterUtil.calculateColorComponent( minColor.getRed(), maxColor.getRed(), x );
147         final int green = ForesterUtil.calculateColorComponent( minColor.getGreen(), maxColor.getGreen(), x );
148         final int blue = ForesterUtil.calculateColorComponent( minColor.getBlue(), maxColor.getBlue(), x );
149         return new Color( red, green, blue );
150     }
151
152     /**
153      * This calculates a color. If value is equal to min the returned color is
154      * minColor, if value is equal to max the returned color is maxColor, if
155      * value is equal to mean the returned color is meanColor, otherwise a color
156      * 'proportional' to value is returned -- either between min-mean or
157      * mean-max
158      * 
159      * @param value
160      *            the value
161      * @param min
162      *            the smallest value
163      * @param max
164      *            the largest value 
165      * @param mean
166      *            the mean/median value 
167      * @param minColor
168      *            the color for min
169      * @param maxColor
170      *            the color for max
171      * @param meanColor
172      *            the color for mean
173      * @return a Color
174      */
175     final public static Color calcColor( double value,
176                                          final double min,
177                                          final double max,
178                                          final double mean,
179                                          final Color minColor,
180                                          final Color maxColor,
181                                          final Color meanColor ) {
182         if ( value < min ) {
183             value = min;
184         }
185         if ( value > max ) {
186             value = max;
187         }
188         if ( value < mean ) {
189             final double x = ForesterUtil.calculateColorFactor( value, mean, min );
190             final int red = ForesterUtil.calculateColorComponent( minColor.getRed(), meanColor.getRed(), x );
191             final int green = ForesterUtil.calculateColorComponent( minColor.getGreen(), meanColor.getGreen(), x );
192             final int blue = ForesterUtil.calculateColorComponent( minColor.getBlue(), meanColor.getBlue(), x );
193             return new Color( red, green, blue );
194         }
195         else if ( value > mean ) {
196             final double x = ForesterUtil.calculateColorFactor( value, max, mean );
197             final int red = ForesterUtil.calculateColorComponent( meanColor.getRed(), maxColor.getRed(), x );
198             final int green = ForesterUtil.calculateColorComponent( meanColor.getGreen(), maxColor.getGreen(), x );
199             final int blue = ForesterUtil.calculateColorComponent( meanColor.getBlue(), maxColor.getBlue(), x );
200             return new Color( red, green, blue );
201         }
202         else {
203             return meanColor;
204         }
205     }
206
207     /**
208      * Helper method for calcColor methods.
209      * 
210      * @param smallercolor_component_x
211      *            color component the smaller color
212      * @param largercolor_component_x
213      *            color component the larger color
214      * @param x
215      *            factor
216      * @return an int representing a color component
217      */
218     final private static int calculateColorComponent( final double smallercolor_component_x,
219                                                       final double largercolor_component_x,
220                                                       final double x ) {
221         return ( int ) ( smallercolor_component_x + ( ( x * ( largercolor_component_x - smallercolor_component_x ) ) / 255.0 ) );
222     }
223
224     /**
225      * Helper method for calcColor methods.
226      * 
227      * 
228      * @param value
229      *            the value
230      * @param larger
231      *            the largest value
232      * @param smaller
233      *            the smallest value
234      * @return a normalized value between larger and smaller
235      */
236     final private static double calculateColorFactor( final double value, final double larger, final double smaller ) {
237         return ( 255.0 * ( value - smaller ) ) / ( larger - smaller );
238     }
239
240     final public static String collapseWhiteSpace( final String s ) {
241         return s.replaceAll( "[\\s]+", " " );
242     }
243
244     final public static String colorToHex( final Color color ) {
245         final String rgb = Integer.toHexString( color.getRGB() );
246         return rgb.substring( 2, rgb.length() );
247     }
248
249     synchronized public static void copyFile( final File in, final File out ) throws IOException {
250         final FileInputStream in_s = new FileInputStream( in );
251         final FileOutputStream out_s = new FileOutputStream( out );
252         try {
253             final byte[] buf = new byte[ 1024 ];
254             int i = 0;
255             while ( ( i = in_s.read( buf ) ) != -1 ) {
256                 out_s.write( buf, 0, i );
257             }
258         }
259         catch ( final IOException e ) {
260             throw e;
261         }
262         finally {
263             if ( in_s != null ) {
264                 in_s.close();
265             }
266             if ( out_s != null ) {
267                 out_s.close();
268             }
269         }
270     }
271
272     final public static int countChars( final String str, final char c ) {
273         int count = 0;
274         for( int i = 0; i < str.length(); ++i ) {
275             if ( str.charAt( i ) == c ) {
276                 ++count;
277             }
278         }
279         return count;
280     }
281
282     final public static BufferedWriter createBufferedWriter( final File file ) throws IOException {
283         if ( file.exists() ) {
284             throw new IOException( "[" + file + "] already exists" );
285         }
286         return new BufferedWriter( new FileWriter( file ) );
287     }
288
289     final public static BufferedWriter createBufferedWriter( final String name ) throws IOException {
290         return new BufferedWriter( new FileWriter( createFileForWriting( name ) ) );
291     }
292
293     final public static File createFileForWriting( final String name ) throws IOException {
294         final File file = new File( name );
295         if ( file.exists() ) {
296             throw new IOException( "[" + name + "] already exists" );
297         }
298         return file;
299     }
300
301     final public static PhylogenyParser createParserDependingFileContents( final File file,
302                                                                            final boolean phyloxml_validate_against_xsd )
303             throws FileNotFoundException, IOException {
304         PhylogenyParser parser = null;
305         final String first_line = ForesterUtil.getFirstLine( file ).trim().toLowerCase();
306         if ( first_line.startsWith( "<" ) ) {
307             parser = new PhyloXmlParser();
308             if ( phyloxml_validate_against_xsd ) {
309                 final ClassLoader cl = PhyloXmlParser.class.getClassLoader();
310                 final URL xsd_url = cl.getResource( ForesterConstants.LOCAL_PHYLOXML_XSD_RESOURCE );
311                 if ( xsd_url != null ) {
312                     ( ( PhyloXmlParser ) parser ).setValidateAgainstSchema( xsd_url.toString() );
313                 }
314                 else {
315                     if ( ForesterConstants.RELEASE ) {
316                         throw new RuntimeException( "failed to get URL for phyloXML XSD from jar file from ["
317                                 + ForesterConstants.LOCAL_PHYLOXML_XSD_RESOURCE + "]" );
318                     }
319                 }
320             }
321         }
322         else if ( ( first_line.startsWith( "nexus" ) ) || ( first_line.startsWith( "#nexus" ) )
323                 || ( first_line.startsWith( "# nexus" ) ) || ( first_line.startsWith( "begin" ) ) ) {
324             parser = new NexusPhylogeniesParser();
325         }
326         else {
327             parser = new NHXParser();
328         }
329         return parser;
330     }
331
332     final public static PhylogenyParser createParserDependingOnFileType( final File file,
333                                                                          final boolean phyloxml_validate_against_xsd )
334             throws FileNotFoundException, IOException {
335         PhylogenyParser parser = null;
336         parser = createParserDependingOnSuffix( file.getName(), phyloxml_validate_against_xsd );
337         if ( parser == null ) {
338             parser = createParserDependingFileContents( file, phyloxml_validate_against_xsd );
339         }
340         return parser;
341     }
342
343     /**
344      * Return null if it can not guess the parser to use based on name suffix.
345      * 
346      * @param filename
347      * @return
348      */
349     final public static PhylogenyParser createParserDependingOnSuffix( final String filename,
350                                                                        final boolean phyloxml_validate_against_xsd ) {
351         PhylogenyParser parser = null;
352         final String filename_lc = filename.toLowerCase();
353         if ( filename_lc.endsWith( ".tol" ) || filename_lc.endsWith( ".tolxml" ) || filename_lc.endsWith( ".tol.zip" ) ) {
354             parser = new TolParser();
355         }
356         else if ( filename_lc.endsWith( ".xml" ) || filename_lc.endsWith( ".px" ) || filename_lc.endsWith( "phyloxml" )
357                 || filename_lc.endsWith( ".zip" ) ) {
358             parser = new PhyloXmlParser();
359             if ( phyloxml_validate_against_xsd ) {
360                 final ClassLoader cl = PhyloXmlParser.class.getClassLoader();
361                 final URL xsd_url = cl.getResource( ForesterConstants.LOCAL_PHYLOXML_XSD_RESOURCE );
362                 if ( xsd_url != null ) {
363                     ( ( PhyloXmlParser ) parser ).setValidateAgainstSchema( xsd_url.toString() );
364                 }
365                 else {
366                     if ( ForesterConstants.RELEASE ) {
367                         throw new RuntimeException( "failed to get URL for phyloXML XSD from jar file from ["
368                                 + ForesterConstants.LOCAL_PHYLOXML_XSD_RESOURCE + "]" );
369                     }
370                 }
371             }
372         }
373         else if ( filename_lc.endsWith( ".nexus" ) || filename_lc.endsWith( ".nex" ) || filename_lc.endsWith( ".nx" ) ) {
374             parser = new NexusPhylogeniesParser();
375         }
376         else if ( filename_lc.endsWith( ".nhx" ) || filename_lc.endsWith( ".nh" ) || filename_lc.endsWith( ".newick" ) ) {
377             parser = new NHXParser();
378         }
379         return parser;
380     }
381
382     final public static PhylogenyParser createParserDependingOnUrlContents( final URL url,
383                                                                             final boolean phyloxml_validate_against_xsd )
384             throws FileNotFoundException, IOException {
385         final String lc_filename = url.getFile().toString().toLowerCase();
386         PhylogenyParser parser = createParserDependingOnSuffix( lc_filename, phyloxml_validate_against_xsd );
387         if ( ( parser != null ) && lc_filename.endsWith( ".zip" ) ) {
388             if ( parser instanceof PhyloXmlParser ) {
389                 ( ( PhyloXmlParser ) parser ).setZippedInputstream( true );
390             }
391             else if ( parser instanceof TolParser ) {
392                 ( ( TolParser ) parser ).setZippedInputstream( true );
393             }
394         }
395         if ( parser == null ) {
396             final String first_line = getFirstLine( url ).trim().toLowerCase();
397             if ( first_line.startsWith( "<" ) ) {
398                 parser = new PhyloXmlParser();
399                 if ( phyloxml_validate_against_xsd ) {
400                     final ClassLoader cl = PhyloXmlParser.class.getClassLoader();
401                     final URL xsd_url = cl.getResource( ForesterConstants.LOCAL_PHYLOXML_XSD_RESOURCE );
402                     if ( xsd_url != null ) {
403                         ( ( PhyloXmlParser ) parser ).setValidateAgainstSchema( xsd_url.toString() );
404                     }
405                     else {
406                         throw new RuntimeException( "failed to get URL for phyloXML XSD from jar file from ["
407                                 + ForesterConstants.LOCAL_PHYLOXML_XSD_RESOURCE + "]" );
408                     }
409                 }
410             }
411             else if ( ( first_line.startsWith( "nexus" ) ) || ( first_line.startsWith( "#nexus" ) )
412                     || ( first_line.startsWith( "# nexus" ) ) || ( first_line.startsWith( "begin" ) ) ) {
413                 parser = new NexusPhylogeniesParser();
414             }
415             else {
416                 parser = new NHXParser();
417             }
418         }
419         return parser;
420     }
421
422     final public static void ensurePresenceOfDate( final PhylogenyNode node ) {
423         if ( !node.getNodeData().isHasDate() ) {
424             node.getNodeData().setDate( new org.forester.phylogeny.data.Date() );
425         }
426     }
427
428     final public static void ensurePresenceOfDistribution( final PhylogenyNode node ) {
429         if ( !node.getNodeData().isHasDistribution() ) {
430             node.getNodeData().setDistribution( new Distribution( "" ) );
431         }
432     }
433
434     public static void ensurePresenceOfSequence( final PhylogenyNode node ) {
435         if ( !node.getNodeData().isHasSequence() ) {
436             node.getNodeData().setSequence( new Sequence() );
437         }
438     }
439
440     public static void ensurePresenceOfTaxonomy( final PhylogenyNode node ) {
441         if ( !node.getNodeData().isHasTaxonomy() ) {
442             node.getNodeData().setTaxonomy( new Taxonomy() );
443         }
444     }
445
446     /**
447      * Extracts a code if and only if:
448      * one and only one _, 
449      * shorter than 25, 
450      * no |, 
451      * no ., 
452      * if / present it has to be after the _, 
453      * if PFAM_STYLE_ONLY: / must be present,
454      * tax code can only contain uppercase letters and numbers,
455      * and must contain at least one uppercase letter.
456      * Return null if no code extractable.
457      * 
458      * @param name
459      * @param limit_to_five
460      * @return
461      */
462     public static String extractTaxonomyCodeFromNodeName( final String name,
463                                                           final boolean limit_to_five,
464                                                           final ForesterUtil.TAXONOMY_EXTRACTION taxonomy_extraction ) {
465         if ( ( name.indexOf( "_" ) > 0 )
466                 && ( name.length() < 25 )
467                 && ( name.lastIndexOf( "_" ) == name.indexOf( "_" ) )
468                 && ( name.indexOf( "|" ) < 0 )
469                 && ( name.indexOf( "." ) < 0 )
470                 && ( ( taxonomy_extraction != ForesterUtil.TAXONOMY_EXTRACTION.PFAM_STYLE_ONLY ) || ( name
471                         .indexOf( "/" ) >= 0 ) )
472                 && ( ( ( name.indexOf( "/" ) ) < 0 ) || ( name.indexOf( "/" ) > name.indexOf( "_" ) ) ) ) {
473             final String[] s = name.split( "[_/]" );
474             if ( s.length > 1 ) {
475                 String str = s[ 1 ];
476                 if ( limit_to_five ) {
477                     if ( str.length() > 5 ) {
478                         str = str.substring( 0, 5 );
479                     }
480                     else if ( ( str.length() < 5 ) && ( str.startsWith( "RAT" ) || str.startsWith( "PIG" ) ) ) {
481                         str = str.substring( 0, 3 );
482                     }
483                 }
484                 final Matcher letters_and_numbers = NHXParser.UC_LETTERS_NUMBERS_PATTERN.matcher( str );
485                 if ( !letters_and_numbers.matches() ) {
486                     return null;
487                 }
488                 final Matcher numbers_only = NHXParser.NUMBERS_ONLY_PATTERN.matcher( str );
489                 if ( numbers_only.matches() ) {
490                     return null;
491                 }
492                 return str;
493             }
494         }
495         return null;
496     }
497
498     public static void fatalError( final String prg_name, final String message ) {
499         System.err.println();
500         System.err.println( "[" + prg_name + "] > " + message );
501         System.err.println();
502         System.exit( -1 );
503     }
504
505     public static String[] file2array( final File file ) throws IOException {
506         final List<String> list = file2list( file );
507         final String[] ary = new String[ list.size() ];
508         int i = 0;
509         for( final String s : list ) {
510             ary[ i++ ] = s;
511         }
512         return ary;
513     }
514
515     final public static List<String> file2list( final File file ) throws IOException {
516         final List<String> list = new ArrayList<String>();
517         final BufferedReader in = new BufferedReader( new FileReader( file ) );
518         String str;
519         while ( ( str = in.readLine() ) != null ) {
520             str = str.trim();
521             if ( ( str.length() > 0 ) && !str.startsWith( "#" ) ) {
522                 for( final String s : splitString( str ) ) {
523                     list.add( s );
524                 }
525             }
526         }
527         in.close();
528         return list;
529     }
530
531     final public static SortedSet<String> file2set( final File file ) throws IOException {
532         final SortedSet<String> set = new TreeSet<String>();
533         final BufferedReader in = new BufferedReader( new FileReader( file ) );
534         String str;
535         while ( ( str = in.readLine() ) != null ) {
536             str = str.trim();
537             if ( ( str.length() > 0 ) && !str.startsWith( "#" ) ) {
538                 for( final String s : splitString( str ) ) {
539                     set.add( s );
540                 }
541             }
542         }
543         in.close();
544         return set;
545     }
546
547     final public static String getCurrentDateTime() {
548         final DateFormat format = new SimpleDateFormat( "yyyy/MM/dd HH:mm:ss" );
549         return format.format( new Date() );
550     }
551
552     final public static String getFileSeparator() {
553         return ForesterUtil.FILE_SEPARATOR;
554     }
555
556     final public static String getFirstLine( final Object source ) throws FileNotFoundException, IOException {
557         BufferedReader reader = null;
558         if ( source instanceof File ) {
559             final File f = ( File ) source;
560             if ( !f.exists() ) {
561                 throw new IOException( "[" + f.getAbsolutePath() + "] does not exist" );
562             }
563             else if ( !f.isFile() ) {
564                 throw new IOException( "[" + f.getAbsolutePath() + "] is not a file" );
565             }
566             else if ( !f.canRead() ) {
567                 throw new IOException( "[" + f.getAbsolutePath() + "] is not a readable" );
568             }
569             reader = new BufferedReader( new FileReader( f ) );
570         }
571         else if ( source instanceof InputStream ) {
572             reader = new BufferedReader( new InputStreamReader( ( InputStream ) source ) );
573         }
574         else if ( source instanceof String ) {
575             reader = new BufferedReader( new StringReader( ( String ) source ) );
576         }
577         else if ( source instanceof StringBuffer ) {
578             reader = new BufferedReader( new StringReader( source.toString() ) );
579         }
580         else if ( source instanceof URL ) {
581             reader = new BufferedReader( new InputStreamReader( ( ( URL ) source ).openStream() ) );
582         }
583         else {
584             throw new IllegalArgumentException( "dont know how to read [" + source.getClass() + "]" );
585         }
586         String line;
587         while ( ( line = reader.readLine() ) != null ) {
588             line = line.trim();
589             if ( !ForesterUtil.isEmpty( line ) ) {
590                 if ( reader != null ) {
591                     reader.close();
592                 }
593                 return line;
594             }
595         }
596         if ( reader != null ) {
597             reader.close();
598         }
599         return line;
600     }
601
602     final public static String getLineSeparator() {
603         return ForesterUtil.LINE_SEPARATOR;
604     }
605
606     /**
607      * Returns all custom data tag names of this Phylogeny as Hashtable. Tag
608      * names are keys, values are Boolean set to false.
609      */
610     final public static Hashtable<String, Boolean> getPropertyRefs( final Phylogeny phylogeny ) {
611         final Hashtable<String, Boolean> ht = new Hashtable<String, Boolean>();
612         if ( phylogeny.isEmpty() ) {
613             return ht;
614         }
615         for( final PhylogenyNodeIterator iter = phylogeny.iteratorPreorder(); iter.hasNext(); ) {
616             final PhylogenyNode current_node = iter.next();
617             if ( current_node.getNodeData().isHasProperties() ) {
618                 final String[] tags = current_node.getNodeData().getProperties().getPropertyRefs();
619                 for( int i = 0; i < tags.length; ++i ) {
620                     ht.put( tags[ i ], new Boolean( false ) );
621                 }
622             }
623         }
624         return ht;
625     }
626
627     final public static void increaseCountingMap( final Map<String, Integer> counting_map, final String item_name ) {
628         if ( !counting_map.containsKey( item_name ) ) {
629             counting_map.put( item_name, 1 );
630         }
631         else {
632             counting_map.put( item_name, counting_map.get( item_name ) + 1 );
633         }
634     }
635
636     final static public boolean isAllNonEmptyInternalLabelsArePositiveNumbers( final Phylogeny phy ) {
637         final PhylogenyNodeIterator it = phy.iteratorPostorder();
638         while ( it.hasNext() ) {
639             final PhylogenyNode n = it.next();
640             if ( !n.isRoot() && !n.isExternal() ) {
641                 if ( !ForesterUtil.isEmpty( n.getName() ) ) {
642                     double d = -1.0;
643                     try {
644                         d = Double.parseDouble( n.getName() );
645                     }
646                     catch ( final Exception e ) {
647                         d = -1.0;
648                     }
649                     if ( d < 0.0 ) {
650                         return false;
651                     }
652                 }
653             }
654         }
655         return true;
656     }
657
658     final public static boolean isContainsParanthesesableNhCharacter( final String nh ) {
659         return PARANTHESESABLE_NH_CHARS_PATTERN.matcher( nh ).find();
660     }
661
662     final public static boolean isEmpty( final List<?> l ) {
663         if ( ( l == null ) || l.isEmpty() ) {
664             return true;
665         }
666         for( final Object o : l ) {
667             if ( o != null ) {
668                 return false;
669             }
670         }
671         return true;
672     }
673
674     final public static boolean isEmpty( final Set<?> s ) {
675         if ( ( s == null ) || s.isEmpty() ) {
676             return true;
677         }
678         for( final Object o : s ) {
679             if ( o != null ) {
680                 return false;
681             }
682         }
683         return true;
684     }
685
686     final public static boolean isEmpty( final String s ) {
687         return ( ( s == null ) || ( s.length() < 1 ) );
688     }
689
690     final public static boolean isEqual( final double a, final double b ) {
691         return ( ( Math.abs( a - b ) ) < ZERO_DIFF );
692     }
693
694     final public static boolean isEven( final int n ) {
695         return n % 2 == 0;
696     }
697
698     final static public boolean isHasAtLeastNodeWithEvent( final Phylogeny phy ) {
699         final PhylogenyNodeIterator it = phy.iteratorPostorder();
700         while ( it.hasNext() ) {
701             if ( it.next().getNodeData().isHasEvent() ) {
702                 return true;
703             }
704         }
705         return false;
706     }
707
708     /**
709      * Returns true if at least one branch has a length larger than zero.
710      * 
711      * 
712      * @param phy
713      */
714     final static public boolean isHasAtLeastOneBranchLengthLargerThanZero( final Phylogeny phy ) {
715         final PhylogenyNodeIterator it = phy.iteratorPostorder();
716         while ( it.hasNext() ) {
717             if ( it.next().getDistanceToParent() > 0.0 ) {
718                 return true;
719             }
720         }
721         return false;
722     }
723
724     final static public boolean isHasAtLeastOneBranchWithSupportValues( final Phylogeny phy ) {
725         final PhylogenyNodeIterator it = phy.iteratorPostorder();
726         while ( it.hasNext() ) {
727             if ( it.next().getBranchData().isHasConfidences() ) {
728                 return true;
729             }
730         }
731         return false;
732     }
733
734     /**
735      * This determines whether String[] a and String[] b have at least one
736      * String in common (intersect). Returns false if at least one String[] is
737      * null or empty.
738      * 
739      * @param a
740      *            a String[] b a String[]
741      * @return true if both a and b or not empty or null and contain at least
742      *         one element in common false otherwise
743      */
744     final public static boolean isIntersecting( final String[] a, final String[] b ) {
745         if ( ( a == null ) || ( b == null ) ) {
746             return false;
747         }
748         if ( ( a.length < 1 ) || ( b.length < 1 ) ) {
749             return false;
750         }
751         for( int i = 0; i < a.length; ++i ) {
752             final String ai = a[ i ];
753             for( int j = 0; j < b.length; ++j ) {
754                 if ( ( ai != null ) && ( b[ j ] != null ) && ai.equals( b[ j ] ) ) {
755                     return true;
756                 }
757             }
758         }
759         return false;
760     }
761
762     final public static double isLargerOrEqualToZero( final double d ) {
763         if ( d > 0.0 ) {
764             return d;
765         }
766         else {
767             return 0.0;
768         }
769     }
770
771     final public static boolean isNull( final BigDecimal s ) {
772         return ( ( s == null ) || ( s.compareTo( NULL_BD ) == 0 ) );
773     }
774
775     final public static String isReadableFile( final File f ) {
776         if ( !f.exists() ) {
777             return "file [" + f + "] does not exist";
778         }
779         if ( f.isDirectory() ) {
780             return "[" + f + "] is a directory";
781         }
782         if ( !f.isFile() ) {
783             return "[" + f + "] is not a file";
784         }
785         if ( !f.canRead() ) {
786             return "file [" + f + "] is not readable";
787         }
788         if ( f.length() < 1 ) {
789             return "file [" + f + "] is empty";
790         }
791         return null;
792     }
793
794     final public static String isReadableFile( final String s ) {
795         return isReadableFile( new File( s ) );
796     }
797
798     final public static String isWritableFile( final File f ) {
799         if ( f.isDirectory() ) {
800             return "[" + f + "] is a directory";
801         }
802         if ( f.exists() ) {
803             return "[" + f + "] already exists";
804         }
805         return null;
806     }
807
808     /**
809      * Helper for method "stringToColor".
810      * <p>
811      * (Last modified: 12/20/03)
812      */
813     final public static int limitRangeForColor( int i ) {
814         if ( i > 255 ) {
815             i = 255;
816         }
817         else if ( i < 0 ) {
818             i = 0;
819         }
820         return i;
821     }
822
823     final public static SortedMap<Object, Integer> listToSortedCountsMap( final List list ) {
824         final SortedMap<Object, Integer> map = new TreeMap<Object, Integer>();
825         for( final Object key : list ) {
826             if ( !map.containsKey( key ) ) {
827                 map.put( key, 1 );
828             }
829             else {
830                 map.put( key, map.get( key ) + 1 );
831             }
832         }
833         return map;
834     }
835
836     final public static StringBuffer mapToStringBuffer( final Map map, final String key_value_separator ) {
837         final StringBuffer sb = new StringBuffer();
838         for( final Iterator iter = map.keySet().iterator(); iter.hasNext(); ) {
839             final Object key = iter.next();
840             sb.append( key.toString() );
841             sb.append( key_value_separator );
842             sb.append( map.get( key ).toString() );
843             sb.append( ForesterUtil.getLineSeparator() );
844         }
845         return sb;
846     }
847
848     final public static String normalizeString( final String s,
849                                                 final int length,
850                                                 final boolean left_pad,
851                                                 final char pad_char ) {
852         if ( s.length() > length ) {
853             return s.substring( 0, length );
854         }
855         else {
856             final StringBuffer pad = new StringBuffer( length - s.length() );
857             for( int i = 0; i < ( length - s.length() ); ++i ) {
858                 pad.append( pad_char );
859             }
860             if ( left_pad ) {
861                 return pad + s;
862             }
863             else {
864                 return s + pad;
865             }
866         }
867     }
868
869     final public static BufferedReader obtainReader( final Object source ) throws IOException, FileNotFoundException {
870         BufferedReader reader = null;
871         if ( source instanceof File ) {
872             final File f = ( File ) source;
873             if ( !f.exists() ) {
874                 throw new IOException( "\"" + f.getAbsolutePath() + "\" does not exist" );
875             }
876             else if ( !f.isFile() ) {
877                 throw new IOException( "\"" + f.getAbsolutePath() + "\" is not a file" );
878             }
879             else if ( !f.canRead() ) {
880                 throw new IOException( "\"" + f.getAbsolutePath() + "\" is not a readable" );
881             }
882             reader = new BufferedReader( new FileReader( f ) );
883         }
884         else if ( source instanceof InputStream ) {
885             reader = new BufferedReader( new InputStreamReader( ( InputStream ) source ) );
886         }
887         else if ( source instanceof String ) {
888             reader = new BufferedReader( new StringReader( ( String ) source ) );
889         }
890         else if ( source instanceof StringBuffer ) {
891             reader = new BufferedReader( new StringReader( source.toString() ) );
892         }
893         else {
894             throw new IllegalArgumentException( "attempt to parse object of type [" + source.getClass()
895                     + "] (can only parse objects of type File, InputStream, String, or StringBuffer)" );
896         }
897         return reader;
898     }
899
900     final public static StringBuffer pad( final double number, final int size, final char pad, final boolean left_pad ) {
901         return pad( new StringBuffer( number + "" ), size, pad, left_pad );
902     }
903
904     final public static StringBuffer pad( final String string, final int size, final char pad, final boolean left_pad ) {
905         return pad( new StringBuffer( string ), size, pad, left_pad );
906     }
907
908     final public static StringBuffer pad( final StringBuffer string,
909                                           final int size,
910                                           final char pad,
911                                           final boolean left_pad ) {
912         final StringBuffer padding = new StringBuffer();
913         final int s = size - string.length();
914         if ( s < 1 ) {
915             return new StringBuffer( string.substring( 0, size ) );
916         }
917         for( int i = 0; i < s; ++i ) {
918             padding.append( pad );
919         }
920         if ( left_pad ) {
921             return padding.append( string );
922         }
923         else {
924             return string.append( padding );
925         }
926     }
927
928     final public static double parseDouble( final String str ) throws ParseException {
929         if ( ForesterUtil.isEmpty( str ) ) {
930             return 0.0;
931         }
932         return Double.parseDouble( str );
933     }
934
935     final public static int parseInt( final String str ) throws ParseException {
936         if ( ForesterUtil.isEmpty( str ) ) {
937             return 0;
938         }
939         return Integer.parseInt( str );
940     }
941
942     final public static void postOrderRelabelInternalNodes( final Phylogeny phylogeny, final int starting_number ) {
943         int i = starting_number;
944         for( final PhylogenyNodeIterator it = phylogeny.iteratorPostorder(); it.hasNext(); ) {
945             final PhylogenyNode node = it.next();
946             if ( !node.isExternal() ) {
947                 node.setName( String.valueOf( i++ ) );
948             }
949         }
950     }
951
952     final public static void printArray( final Object[] a ) {
953         for( int i = 0; i < a.length; ++i ) {
954             System.out.println( "[" + i + "]=" + a[ i ] );
955         }
956     }
957
958     final public static void printCountingMap( final Map<String, Integer> counting_map ) {
959         for( final String key : counting_map.keySet() ) {
960             System.out.println( key + ": " + counting_map.get( key ) );
961         }
962     }
963
964     final public static void printErrorMessage( final String prg_name, final String message ) {
965         System.out.println( "[" + prg_name + "] > error: " + message );
966     }
967
968     final public static void printProgramInformation( final String prg_name, final String prg_version, final String date ) {
969         final int l = prg_name.length() + prg_version.length() + date.length() + 4;
970         System.out.println();
971         System.out.println( prg_name + " " + prg_version + " (" + date + ")" );
972         for( int i = 0; i < l; ++i ) {
973             System.out.print( "_" );
974         }
975         System.out.println();
976     }
977
978     final public static void printProgramInformation( final String prg_name,
979                                                       final String prg_version,
980                                                       final String date,
981                                                       final String email,
982                                                       final String www ) {
983         final int l = prg_name.length() + prg_version.length() + date.length() + 4;
984         System.out.println();
985         System.out.println( prg_name + " " + prg_version + " (" + date + ")" );
986         for( int i = 0; i < l; ++i ) {
987             System.out.print( "_" );
988         }
989         System.out.println();
990         System.out.println();
991         System.out.println( "WWW    : " + www );
992         System.out.println( "Contact: " + email );
993         if ( !ForesterUtil.isEmpty( ForesterUtil.JAVA_VERSION ) && !ForesterUtil.isEmpty( ForesterUtil.JAVA_VENDOR ) ) {
994             System.out.println();
995             System.out.println( "[running on Java " + ForesterUtil.JAVA_VERSION + " " + ForesterUtil.JAVA_VENDOR + "]" );
996         }
997         System.out.println();
998     }
999
1000     final public static void printWarningMessage( final String prg_name, final String message ) {
1001         System.out.println( "[" + prg_name + "] > warning: " + message );
1002     }
1003
1004     final public static void programMessage( final String prg_name, final String message ) {
1005         System.out.println( "[" + prg_name + "] > " + message );
1006     }
1007
1008     public final static Phylogeny[] readPhylogenies( final PhylogenyParser parser, final File file ) throws IOException {
1009         final PhylogenyFactory factory = ParserBasedPhylogenyFactory.getInstance();
1010         final Phylogeny[] trees = factory.create( file, parser );
1011         if ( ( trees == null ) || ( trees.length == 0 ) ) {
1012             throw new PhylogenyParserException( "Unable to parse phylogeny from file: " + file );
1013         }
1014         return trees;
1015     }
1016
1017     final public static String removeSuffix( final String file_name ) {
1018         final int i = file_name.lastIndexOf( '.' );
1019         if ( i > 1 ) {
1020             return file_name.substring( 0, i );
1021         }
1022         return file_name;
1023     }
1024
1025     /**
1026      * Removes all white space from String s.
1027      * 
1028      * @return String s with white space removed
1029      */
1030     final public static String removeWhiteSpace( String s ) {
1031         int i;
1032         for( i = 0; i <= s.length() - 1; i++ ) {
1033             if ( ( s.charAt( i ) == ' ' ) || ( s.charAt( i ) == '\t' ) || ( s.charAt( i ) == '\n' )
1034                     || ( s.charAt( i ) == '\r' ) ) {
1035                 s = s.substring( 0, i ) + s.substring( i + 1 );
1036                 i--;
1037             }
1038         }
1039         return s;
1040     }
1041
1042     final public static String replaceIllegalNhCharacters( final String nh ) {
1043         if ( nh == null ) {
1044             return "";
1045         }
1046         return nh.trim().replaceAll( "[\\[\\]:]+", "_" );
1047     }
1048
1049     final public static String replaceIllegalNhxCharacters( final String nhx ) {
1050         if ( nhx == null ) {
1051             return "";
1052         }
1053         return nhx.trim().replaceAll( "[\\[\\](),:;\\s]+", "_" );
1054     }
1055
1056     final public static double round( final double value, final int decimal_place ) {
1057         BigDecimal bd = new BigDecimal( value );
1058         bd = bd.setScale( decimal_place, BigDecimal.ROUND_HALF_UP );
1059         return bd.doubleValue();
1060     }
1061
1062     /**
1063      * Rounds d to an int.
1064      */
1065     final public static int roundToInt( final double d ) {
1066         return ( int ) ( d + 0.5 );
1067     }
1068
1069     final public static int roundToInt( final float f ) {
1070         return ( int ) ( f + 0.5f );
1071     }
1072
1073     final public static short roundToShort( final double d ) {
1074         return ( short ) ( d + 0.5 );
1075     }
1076
1077     final public static String sanitizeString( final String s ) {
1078         if ( s == null ) {
1079             return "";
1080         }
1081         else {
1082             return s.trim();
1083         }
1084     }
1085
1086     final private static String[] splitString( final String str ) {
1087         final String regex = "[\\s;,]+";
1088         return str.split( regex );
1089     }
1090
1091     final public static String stringArrayToString( final String[] a, final String separator ) {
1092         final StringBuilder sb = new StringBuilder();
1093         if ( ( a != null ) && ( a.length > 0 ) ) {
1094             for( int i = 0; i < a.length - 1; ++i ) {
1095                 sb.append( a[ i ] + separator );
1096             }
1097             sb.append( a[ a.length - 1 ] );
1098         }
1099         return sb.toString();
1100     }
1101
1102     final public static String stringListToString( final List<String> l, final String separator ) {
1103         final StringBuilder sb = new StringBuilder();
1104         if ( ( l != null ) && ( l.size() > 0 ) ) {
1105             for( int i = 0; i < l.size() - 1; ++i ) {
1106                 sb.append( l.get( i ) + separator );
1107             }
1108             sb.append( l.get( l.size() - 1 ) );
1109         }
1110         return sb.toString();
1111     }
1112
1113     final public static String stringArrayToString( final String[] a ) {
1114         return stringArrayToString( a, ", " );
1115     }
1116
1117     final public static String[] stringSetToArray( final Set<String> strings ) {
1118         final String[] str_array = new String[ strings.size() ];
1119         int i = 0;
1120         for( final String e : strings ) {
1121             str_array[ i++ ] = e;
1122         }
1123         return str_array;
1124     }
1125
1126     final public static String[] stringListToArray( final List<String> list ) {
1127         if ( list != null ) {
1128             final String[] str = new String[ list.size() ];
1129             int i = 0;
1130             for( final String l : list ) {
1131                 str[ i++ ] = l;
1132             }
1133             return str;
1134         }
1135         return null;
1136     }
1137
1138     final static public void transferInternalNamesToBootstrapSupport( final Phylogeny phy ) {
1139         final PhylogenyNodeIterator it = phy.iteratorPostorder();
1140         while ( it.hasNext() ) {
1141             final PhylogenyNode n = it.next();
1142             if ( !n.isExternal() && !ForesterUtil.isEmpty( n.getName() ) ) {
1143                 double value = -1;
1144                 try {
1145                     value = Double.parseDouble( n.getName() );
1146                 }
1147                 catch ( final NumberFormatException e ) {
1148                     throw new IllegalArgumentException( "failed to parse number from [" + n.getName() + "]: "
1149                             + e.getLocalizedMessage() );
1150                 }
1151                 if ( value >= 0.0 ) {
1152                     n.getBranchData().addConfidence( new Confidence( value, "bootstrap" ) );
1153                     n.setName( "" );
1154                 }
1155             }
1156         }
1157     }
1158
1159     final static public void transferInternalNodeNamesToConfidence( final Phylogeny phy ) {
1160         final PhylogenyNodeIterator it = phy.iteratorPostorder();
1161         while ( it.hasNext() ) {
1162             final PhylogenyNode n = it.next();
1163             if ( !n.isRoot() && !n.isExternal() && !n.getBranchData().isHasConfidences() ) {
1164                 if ( !ForesterUtil.isEmpty( n.getName() ) ) {
1165                     double d = -1.0;
1166                     try {
1167                         d = Double.parseDouble( n.getName() );
1168                     }
1169                     catch ( final Exception e ) {
1170                         d = -1.0;
1171                     }
1172                     if ( d >= 0.0 ) {
1173                         n.getBranchData().addConfidence( new Confidence( d, "" ) );
1174                         n.setName( "" );
1175                     }
1176                 }
1177             }
1178         }
1179     }
1180
1181     final static public void transferNodeNameToField( final Phylogeny phy, final PhylogenyNodeField field ) {
1182         final PhylogenyNodeIterator it = phy.iteratorPostorder();
1183         while ( it.hasNext() ) {
1184             final PhylogenyNode n = it.next();
1185             final String name = n.getName().trim();
1186             if ( !ForesterUtil.isEmpty( name ) ) {
1187                 switch ( field ) {
1188                     case TAXONOMY_CODE:
1189                         //temp hack
1190                         //                        if ( name.length() > 5 ) {
1191                         //                            n.setName( "" );
1192                         //                            if ( !n.getNodeData().isHasTaxonomy() ) {
1193                         //                                n.getNodeData().setTaxonomy( new Taxonomy() );
1194                         //                            }
1195                         //                            n.getNodeData().getTaxonomy().setScientificName( name );
1196                         //                            break;
1197                         //                        }
1198                         //
1199                         n.setName( "" );
1200                         PhylogenyMethods.setTaxonomyCode( n, name );
1201                         break;
1202                     case TAXONOMY_SCIENTIFIC_NAME:
1203                         n.setName( "" );
1204                         if ( !n.getNodeData().isHasTaxonomy() ) {
1205                             n.getNodeData().setTaxonomy( new Taxonomy() );
1206                         }
1207                         n.getNodeData().getTaxonomy().setScientificName( name );
1208                         break;
1209                     case TAXONOMY_COMMON_NAME:
1210                         n.setName( "" );
1211                         if ( !n.getNodeData().isHasTaxonomy() ) {
1212                             n.getNodeData().setTaxonomy( new Taxonomy() );
1213                         }
1214                         n.getNodeData().getTaxonomy().setCommonName( name );
1215                         break;
1216                     case SEQUENCE_SYMBOL:
1217                         n.setName( "" );
1218                         if ( !n.getNodeData().isHasSequence() ) {
1219                             n.getNodeData().setSequence( new Sequence() );
1220                         }
1221                         n.getNodeData().getSequence().setSymbol( name );
1222                         break;
1223                     case SEQUENCE_NAME:
1224                         n.setName( "" );
1225                         if ( !n.getNodeData().isHasSequence() ) {
1226                             n.getNodeData().setSequence( new Sequence() );
1227                         }
1228                         n.getNodeData().getSequence().setName( name );
1229                         break;
1230                     case TAXONOMY_ID_UNIPROT_1: {
1231                         if ( !n.getNodeData().isHasTaxonomy() ) {
1232                             n.getNodeData().setTaxonomy( new Taxonomy() );
1233                         }
1234                         String id = name;
1235                         final int i = name.indexOf( '_' );
1236                         if ( i > 0 ) {
1237                             id = name.substring( 0, i );
1238                         }
1239                         else {
1240                             n.setName( "" );
1241                         }
1242                         n.getNodeData().getTaxonomy()
1243                                 .setIdentifier( new Identifier( id, PhyloXmlUtil.UNIPROT_TAX_PROVIDER ) );
1244                         break;
1245                     }
1246                     case TAXONOMY_ID_UNIPROT_2: {
1247                         if ( !n.getNodeData().isHasTaxonomy() ) {
1248                             n.getNodeData().setTaxonomy( new Taxonomy() );
1249                         }
1250                         String id = name;
1251                         final int i = name.indexOf( '_' );
1252                         if ( i > 0 ) {
1253                             id = name.substring( i + 1, name.length() );
1254                         }
1255                         else {
1256                             n.setName( "" );
1257                         }
1258                         n.getNodeData().getTaxonomy()
1259                                 .setIdentifier( new Identifier( id, PhyloXmlUtil.UNIPROT_TAX_PROVIDER ) );
1260                         break;
1261                     }
1262                 }
1263             }
1264         }
1265     }
1266
1267     final public static void unexpectedFatalError( final String prg_name, final Exception e ) {
1268         System.err.println();
1269         System.err.println( "[" + prg_name
1270                 + "] > unexpected error (Should not have occured! Please contact program author(s).)" );
1271         e.printStackTrace( System.err );
1272         System.err.println();
1273         System.exit( -1 );
1274     }
1275
1276     final public static void unexpectedFatalError( final String prg_name, final String message ) {
1277         System.err.println();
1278         System.err.println( "[" + prg_name
1279                 + "] > unexpected error. Should not have occured! Please contact program author(s)." );
1280         System.err.println( message );
1281         System.err.println();
1282         System.exit( -1 );
1283     }
1284
1285     final public static void unexpectedFatalError( final String prg_name, final String message, final Exception e ) {
1286         System.err.println();
1287         System.err.println( "[" + prg_name
1288                 + "] > unexpected error. Should not have occured! Please contact program author(s)." );
1289         System.err.println( message );
1290         e.printStackTrace( System.err );
1291         System.err.println();
1292         System.exit( -1 );
1293     }
1294
1295     public final static String wordWrap( final String str, final int width ) {
1296         final StringBuilder sb = new StringBuilder( str );
1297         int start = 0;
1298         int ls = -1;
1299         int i = 0;
1300         while ( i < sb.length() ) {
1301             if ( sb.charAt( i ) == ' ' ) {
1302                 ls = i;
1303             }
1304             if ( sb.charAt( i ) == '\n' ) {
1305                 ls = -1;
1306                 start = i + 1;
1307             }
1308             if ( i > start + width - 1 ) {
1309                 if ( ls != -1 ) {
1310                     sb.setCharAt( ls, '\n' );
1311                     start = ls + 1;
1312                     ls = -1;
1313                 }
1314                 else {
1315                     sb.insert( i, '\n' );
1316                     start = i + 1;
1317                 }
1318             }
1319             i++;
1320         }
1321         return sb.toString();
1322     }
1323
1324     public static enum PhylogenyNodeField {
1325         CLADE_NAME,
1326         TAXONOMY_CODE,
1327         TAXONOMY_SCIENTIFIC_NAME,
1328         TAXONOMY_COMMON_NAME,
1329         SEQUENCE_SYMBOL,
1330         SEQUENCE_NAME,
1331         TAXONOMY_ID_UNIPROT_1,
1332         TAXONOMY_ID_UNIPROT_2;
1333     }
1334
1335     public static enum TAXONOMY_EXTRACTION {
1336         NO, YES, PFAM_STYLE_ONLY;
1337     }
1338 }