in progress
[jalview.git] / forester / java / src / org / forester / phylogeny / PhylogenyMethods.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.phylogeny;
27
28 import java.awt.Color;
29 import java.io.File;
30 import java.io.IOException;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.HashSet;
34 import java.util.Iterator;
35 import java.util.List;
36 import java.util.Set;
37 import java.util.SortedMap;
38 import java.util.TreeMap;
39
40 import org.forester.io.parsers.PhylogenyParser;
41 import org.forester.io.parsers.phyloxml.PhyloXmlUtil;
42 import org.forester.io.parsers.util.PhylogenyParserException;
43 import org.forester.phylogeny.data.BranchColor;
44 import org.forester.phylogeny.data.BranchWidth;
45 import org.forester.phylogeny.data.Confidence;
46 import org.forester.phylogeny.data.DomainArchitecture;
47 import org.forester.phylogeny.data.Identifier;
48 import org.forester.phylogeny.data.PhylogenyDataUtil;
49 import org.forester.phylogeny.data.Sequence;
50 import org.forester.phylogeny.data.Taxonomy;
51 import org.forester.phylogeny.factories.ParserBasedPhylogenyFactory;
52 import org.forester.phylogeny.factories.PhylogenyFactory;
53 import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
54 import org.forester.util.BasicDescriptiveStatistics;
55 import org.forester.util.DescriptiveStatistics;
56 import org.forester.util.FailedConditionCheckException;
57 import org.forester.util.ForesterUtil;
58
59 public class PhylogenyMethods {
60
61     private static PhylogenyMethods _instance      = null;
62     private final Set<Integer>      _temp_hash_set = new HashSet<Integer>();
63     private PhylogenyNode           _farthest_1    = null;
64     private PhylogenyNode           _farthest_2    = null;
65
66     private PhylogenyMethods() {
67         // Hidden constructor.
68     }
69
70     /**
71      * Calculates the distance between PhylogenyNodes node1 and node2.
72      * 
73      * 
74      * @param node1
75      * @param node2
76      * @return distance between node1 and node2
77      */
78     public double calculateDistance( final PhylogenyNode node1, final PhylogenyNode node2 ) {
79         final PhylogenyNode lca = obtainLCA( node1, node2 );
80         final PhylogenyNode n1 = node1;
81         final PhylogenyNode n2 = node2;
82         return ( PhylogenyMethods.getDistance( n1, lca ) + PhylogenyMethods.getDistance( n2, lca ) );
83     }
84
85     public double calculateFurthestDistance( final Phylogeny phylogeny ) {
86         if ( phylogeny.getNumberOfExternalNodes() < 2 ) {
87             return 0.0;
88         }
89         _farthest_1 = null;
90         _farthest_2 = null;
91         PhylogenyNode node_1 = null;
92         PhylogenyNode node_2 = null;
93         double farthest_d = -Double.MAX_VALUE;
94         final PhylogenyMethods methods = PhylogenyMethods.getInstance();
95         final List<PhylogenyNode> ext_nodes = phylogeny.getRoot().getAllExternalDescendants();
96         for( int i = 1; i < ext_nodes.size(); ++i ) {
97             for( int j = 0; j < i; ++j ) {
98                 final double d = methods.calculateDistance( ext_nodes.get( i ), ext_nodes.get( j ) );
99                 if ( d < 0.0 ) {
100                     throw new RuntimeException( "distance cannot be negative" );
101                 }
102                 if ( d > farthest_d ) {
103                     farthest_d = d;
104                     node_1 = ext_nodes.get( i );
105                     node_2 = ext_nodes.get( j );
106                 }
107             }
108         }
109         _farthest_1 = node_1;
110         _farthest_2 = node_2;
111         return farthest_d;
112     }
113
114     @Override
115     public Object clone() throws CloneNotSupportedException {
116         throw new CloneNotSupportedException();
117     }
118
119     public PhylogenyNode getFarthestNode1() {
120         return _farthest_1;
121     }
122
123     public PhylogenyNode getFarthestNode2() {
124         return _farthest_2;
125     }
126
127     /**
128      * Returns the LCA of PhylogenyNodes node1 and node2.
129      * 
130      * 
131      * @param node1
132      * @param node2
133      * @return LCA of node1 and node2
134      */
135     public PhylogenyNode obtainLCA( final PhylogenyNode node1, final PhylogenyNode node2 ) {
136         _temp_hash_set.clear();
137         PhylogenyNode n1 = node1;
138         PhylogenyNode n2 = node2;
139         _temp_hash_set.add( n1.getId() );
140         while ( !n1.isRoot() ) {
141             n1 = n1.getParent();
142             _temp_hash_set.add( n1.getId() );
143         }
144         while ( !_temp_hash_set.contains( n2.getId() ) && !n2.isRoot() ) {
145             n2 = n2.getParent();
146         }
147         if ( !_temp_hash_set.contains( n2.getId() ) ) {
148             throw new IllegalArgumentException( "attempt to get LCA of two nodes which do not share a common root" );
149         }
150         return n2;
151     }
152
153     /**
154      * Returns all orthologs of the external PhylogenyNode n of this Phylogeny.
155      * Orthologs are returned as List of node references.
156      * <p>
157      * PRECONDITION: This tree must be binary and rooted, and speciation -
158      * duplication need to be assigned for each of its internal Nodes.
159      * <p>
160      * Returns null if this Phylogeny is empty or if n is internal.
161      * @param n
162      *            external PhylogenyNode whose orthologs are to be returned
163      * @return Vector of references to all orthologous Nodes of PhylogenyNode n
164      *         of this Phylogeny, null if this Phylogeny is empty or if n is
165      *         internal
166      */
167     public List<PhylogenyNode> getOrthologousNodes( final Phylogeny phy, final PhylogenyNode node ) {
168         final List<PhylogenyNode> nodes = new ArrayList<PhylogenyNode>();
169         final PhylogenyNodeIterator it = phy.iteratorExternalForward();
170         while ( it.hasNext() ) {
171             final PhylogenyNode temp_node = it.next();
172             if ( ( temp_node != node ) && isAreOrthologous( node, temp_node ) ) {
173                 nodes.add( temp_node );
174             }
175         }
176         return nodes;
177     }
178
179     public boolean isAreOrthologous( final PhylogenyNode node1, final PhylogenyNode node2 ) {
180         return !obtainLCA( node1, node2 ).isDuplication();
181     }
182
183     public final static Phylogeny[] readPhylogenies( final PhylogenyParser parser, final File file ) throws IOException {
184         final PhylogenyFactory factory = ParserBasedPhylogenyFactory.getInstance();
185         final Phylogeny[] trees = factory.create( file, parser );
186         if ( ( trees == null ) || ( trees.length == 0 ) ) {
187             throw new PhylogenyParserException( "Unable to parse phylogeny from file: " + file );
188         }
189         return trees;
190     }
191
192     final static public void transferInternalNodeNamesToConfidence( final Phylogeny phy ) {
193         final PhylogenyNodeIterator it = phy.iteratorPostorder();
194         while ( it.hasNext() ) {
195             final PhylogenyNode n = it.next();
196             if ( !n.isExternal() && !n.getBranchData().isHasConfidences() ) {
197                 if ( !ForesterUtil.isEmpty( n.getName() ) ) {
198                     double d = -1.0;
199                     try {
200                         d = Double.parseDouble( n.getName() );
201                     }
202                     catch ( final Exception e ) {
203                         d = -1.0;
204                     }
205                     if ( d >= 0.0 ) {
206                         n.getBranchData().addConfidence( new Confidence( d, "" ) );
207                         n.setName( "" );
208                     }
209                 }
210             }
211         }
212     }
213
214     final static public void transferInternalNamesToBootstrapSupport( final Phylogeny phy ) {
215         final PhylogenyNodeIterator it = phy.iteratorPostorder();
216         while ( it.hasNext() ) {
217             final PhylogenyNode n = it.next();
218             if ( !n.isExternal() && !ForesterUtil.isEmpty( n.getName() ) ) {
219                 double value = -1;
220                 try {
221                     value = Double.parseDouble( n.getName() );
222                 }
223                 catch ( final NumberFormatException e ) {
224                     throw new IllegalArgumentException( "failed to parse number from [" + n.getName() + "]: "
225                             + e.getLocalizedMessage() );
226                 }
227                 if ( value >= 0.0 ) {
228                     n.getBranchData().addConfidence( new Confidence( value, "bootstrap" ) );
229                     n.setName( "" );
230                 }
231             }
232         }
233     }
234
235     final static public void transferNodeNameToField( final Phylogeny phy,
236                                                       final PhylogenyMethods.PhylogenyNodeField field ) {
237         final PhylogenyNodeIterator it = phy.iteratorPostorder();
238         while ( it.hasNext() ) {
239             final PhylogenyNode n = it.next();
240             final String name = n.getName().trim();
241             if ( !ForesterUtil.isEmpty( name ) ) {
242                 switch ( field ) {
243                     case TAXONOMY_CODE:
244                         //temp hack
245                         //                        if ( name.length() > 5 ) {
246                         //                            n.setName( "" );
247                         //                            if ( !n.getNodeData().isHasTaxonomy() ) {
248                         //                                n.getNodeData().setTaxonomy( new Taxonomy() );
249                         //                            }
250                         //                            n.getNodeData().getTaxonomy().setScientificName( name );
251                         //                            break;
252                         //                        }
253                         //
254                         n.setName( "" );
255                         setTaxonomyCode( n, name );
256                         break;
257                     case TAXONOMY_SCIENTIFIC_NAME:
258                         n.setName( "" );
259                         if ( !n.getNodeData().isHasTaxonomy() ) {
260                             n.getNodeData().setTaxonomy( new Taxonomy() );
261                         }
262                         n.getNodeData().getTaxonomy().setScientificName( name );
263                         break;
264                     case TAXONOMY_COMMON_NAME:
265                         n.setName( "" );
266                         if ( !n.getNodeData().isHasTaxonomy() ) {
267                             n.getNodeData().setTaxonomy( new Taxonomy() );
268                         }
269                         n.getNodeData().getTaxonomy().setCommonName( name );
270                         break;
271                     case SEQUENCE_SYMBOL:
272                         n.setName( "" );
273                         if ( !n.getNodeData().isHasSequence() ) {
274                             n.getNodeData().setSequence( new Sequence() );
275                         }
276                         n.getNodeData().getSequence().setSymbol( name );
277                         break;
278                     case SEQUENCE_NAME:
279                         n.setName( "" );
280                         if ( !n.getNodeData().isHasSequence() ) {
281                             n.getNodeData().setSequence( new Sequence() );
282                         }
283                         n.getNodeData().getSequence().setName( name );
284                         break;
285                     case TAXONOMY_ID_UNIPROT_1: {
286                         if ( !n.getNodeData().isHasTaxonomy() ) {
287                             n.getNodeData().setTaxonomy( new Taxonomy() );
288                         }
289                         String id = name;
290                         final int i = name.indexOf( '_' );
291                         if ( i > 0 ) {
292                             id = name.substring( 0, i );
293                         }
294                         else {
295                             n.setName( "" );
296                         }
297                         n.getNodeData().getTaxonomy()
298                                 .setIdentifier( new Identifier( id, PhyloXmlUtil.UNIPROT_TAX_PROVIDER ) );
299                         break;
300                     }
301                     case TAXONOMY_ID_UNIPROT_2: {
302                         if ( !n.getNodeData().isHasTaxonomy() ) {
303                             n.getNodeData().setTaxonomy( new Taxonomy() );
304                         }
305                         String id = name;
306                         final int i = name.indexOf( '_' );
307                         if ( i > 0 ) {
308                             id = name.substring( i + 1, name.length() );
309                         }
310                         else {
311                             n.setName( "" );
312                         }
313                         n.getNodeData().getTaxonomy()
314                                 .setIdentifier( new Identifier( id, PhyloXmlUtil.UNIPROT_TAX_PROVIDER ) );
315                         break;
316                     }
317                 }
318             }
319         }
320     }
321
322     static double addPhylogenyDistances( final double a, final double b ) {
323         if ( ( a >= 0.0 ) && ( b >= 0.0 ) ) {
324             return a + b;
325         }
326         else if ( a >= 0.0 ) {
327             return a;
328         }
329         else if ( b >= 0.0 ) {
330             return b;
331         }
332         return PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT;
333     }
334
335     // Helper for getUltraParalogousNodes( PhylogenyNode ).
336     public static boolean areAllChildrenDuplications( final PhylogenyNode n ) {
337         if ( n.isExternal() ) {
338             return false;
339         }
340         else {
341             if ( n.isDuplication() ) {
342                 //FIXME test me!
343                 for( final PhylogenyNode desc : n.getDescendants() ) {
344                     if ( !areAllChildrenDuplications( desc ) ) {
345                         return false;
346                     }
347                 }
348                 return true;
349             }
350             else {
351                 return false;
352             }
353         }
354     }
355
356     public static int calculateDepth( final PhylogenyNode node ) {
357         PhylogenyNode n = node;
358         int steps = 0;
359         while ( !n.isRoot() ) {
360             steps++;
361             n = n.getParent();
362         }
363         return steps;
364     }
365
366     public static double calculateDistanceToRoot( final PhylogenyNode node ) {
367         PhylogenyNode n = node;
368         double d = 0.0;
369         while ( !n.isRoot() ) {
370             if ( n.getDistanceToParent() > 0.0 ) {
371                 d += n.getDistanceToParent();
372             }
373             n = n.getParent();
374         }
375         return d;
376     }
377
378     public static short calculateMaxBranchesToLeaf( final PhylogenyNode node ) {
379         if ( node.isExternal() ) {
380             return 0;
381         }
382         short max = 0;
383         for( PhylogenyNode d : node.getAllExternalDescendants() ) {
384             short steps = 0;
385             while ( d != node ) {
386                 if ( d.isCollapse() ) {
387                     steps = 0;
388                 }
389                 else {
390                     steps++;
391                 }
392                 d = d.getParent();
393             }
394             if ( max < steps ) {
395                 max = steps;
396             }
397         }
398         return max;
399     }
400
401     public static int calculateMaxDepth( final Phylogeny phy ) {
402         int max = 0;
403         for( final PhylogenyNodeIterator iter = phy.iteratorExternalForward(); iter.hasNext(); ) {
404             final PhylogenyNode node = iter.next();
405             final int steps = calculateDepth( node );
406             if ( steps > max ) {
407                 max = steps;
408             }
409         }
410         return max;
411     }
412
413     public static double calculateMaxDistanceToRoot( final Phylogeny phy ) {
414         double max = 0.0;
415         for( final PhylogenyNodeIterator iter = phy.iteratorExternalForward(); iter.hasNext(); ) {
416             final PhylogenyNode node = iter.next();
417             final double d = calculateDistanceToRoot( node );
418             if ( d > max ) {
419                 max = d;
420             }
421         }
422         return max;
423     }
424
425     public static DescriptiveStatistics calculatNumberOfDescendantsPerNodeStatistics( final Phylogeny phy ) {
426         final DescriptiveStatistics stats = new BasicDescriptiveStatistics();
427         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {
428             final PhylogenyNode n = iter.next();
429             if ( !n.isExternal() ) {
430                 stats.addValue( n.getNumberOfDescendants() );
431             }
432         }
433         return stats;
434     }
435
436     public static DescriptiveStatistics calculatConfidenceStatistics( final Phylogeny phy ) {
437         final DescriptiveStatistics stats = new BasicDescriptiveStatistics();
438         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {
439             final PhylogenyNode n = iter.next();
440             if ( !n.isExternal() ) {
441                 if ( n.getBranchData().isHasConfidences() ) {
442                     stats.addValue( n.getBranchData().getConfidence( 0 ).getValue() );
443                 }
444             }
445         }
446         return stats;
447     }
448
449     /**
450      * Returns the set of distinct taxonomies of
451      * all external nodes of node.
452      * If at least one the external nodes has no taxonomy,
453      * null is returned.
454      * 
455      */
456     public static Set<Taxonomy> obtainDistinctTaxonomies( final PhylogenyNode node ) {
457         final List<PhylogenyNode> descs = node.getAllExternalDescendants();
458         final Set<Taxonomy> tax_set = new HashSet<Taxonomy>();
459         for( final PhylogenyNode n : descs ) {
460             if ( !n.getNodeData().isHasTaxonomy() || n.getNodeData().getTaxonomy().isEmpty() ) {
461                 return null;
462             }
463             tax_set.add( n.getNodeData().getTaxonomy() );
464         }
465         return tax_set;
466     }
467
468     /**
469      * Returns a map of distinct taxonomies of
470      * all external nodes of node.
471      * If at least one of the external nodes has no taxonomy,
472      * null is returned.
473      * 
474      */
475     public static SortedMap<Taxonomy, Integer> obtainDistinctTaxonomyCounts( final PhylogenyNode node ) {
476         final List<PhylogenyNode> descs = node.getAllExternalDescendants();
477         final SortedMap<Taxonomy, Integer> tax_map = new TreeMap<Taxonomy, Integer>();
478         for( final PhylogenyNode n : descs ) {
479             if ( !n.getNodeData().isHasTaxonomy() || n.getNodeData().getTaxonomy().isEmpty() ) {
480                 return null;
481             }
482             final Taxonomy t = n.getNodeData().getTaxonomy();
483             if ( tax_map.containsKey( t ) ) {
484                 tax_map.put( t, tax_map.get( t ) + 1 );
485             }
486             else {
487                 tax_map.put( t, 1 );
488             }
489         }
490         return tax_map;
491     }
492
493     public static int calculateNumberOfExternalNodesWithoutTaxonomy( final PhylogenyNode node ) {
494         final List<PhylogenyNode> descs = node.getAllExternalDescendants();
495         int x = 0;
496         for( final PhylogenyNode n : descs ) {
497             if ( !n.getNodeData().isHasTaxonomy() || n.getNodeData().getTaxonomy().isEmpty() ) {
498                 x++;
499             }
500         }
501         return x;
502     }
503
504     /**
505      * Deep copies the phylogeny originating from this node.
506      */
507     static PhylogenyNode copySubTree( final PhylogenyNode source ) {
508         if ( source == null ) {
509             return null;
510         }
511         else {
512             final PhylogenyNode newnode = source.copyNodeData();
513             if ( !source.isExternal() ) {
514                 for( int i = 0; i < source.getNumberOfDescendants(); ++i ) {
515                     newnode.setChildNode( i, PhylogenyMethods.copySubTree( source.getChildNode( i ) ) );
516                 }
517             }
518             return newnode;
519         }
520     }
521
522     /**
523      * Shallow copies the phylogeny originating from this node.
524      */
525     static PhylogenyNode copySubTreeShallow( final PhylogenyNode source ) {
526         if ( source == null ) {
527             return null;
528         }
529         else {
530             final PhylogenyNode newnode = source.copyNodeDataShallow();
531             if ( !source.isExternal() ) {
532                 for( int i = 0; i < source.getNumberOfDescendants(); ++i ) {
533                     newnode.setChildNode( i, PhylogenyMethods.copySubTreeShallow( source.getChildNode( i ) ) );
534                 }
535             }
536             return newnode;
537         }
538     }
539
540     public static void deleteExternalNodesNegativeSelection( final Set<Integer> to_delete, final Phylogeny phy ) {
541         phy.hashIDs();
542         for( final Integer id : to_delete ) {
543             phy.deleteSubtree( phy.getNode( id ), true );
544         }
545         phy.hashIDs();
546     }
547
548     public static void deleteExternalNodesNegativeSelection( final String[] node_names_to_delete, final Phylogeny p )
549             throws IllegalArgumentException {
550         for( int i = 0; i < node_names_to_delete.length; ++i ) {
551             if ( ForesterUtil.isEmpty( node_names_to_delete[ i ] ) ) {
552                 continue;
553             }
554             List<PhylogenyNode> nodes = null;
555             nodes = p.getNodes( node_names_to_delete[ i ] );
556             final Iterator<PhylogenyNode> it = nodes.iterator();
557             while ( it.hasNext() ) {
558                 final PhylogenyNode n = it.next();
559                 if ( !n.isExternal() ) {
560                     throw new IllegalArgumentException( "attempt to delete non-external node \""
561                             + node_names_to_delete[ i ] + "\"" );
562                 }
563                 p.deleteSubtree( n, true );
564             }
565         }
566     }
567
568     public static void deleteExternalNodesPositiveSelection( final Set<Taxonomy> species_to_keep, final Phylogeny phy ) {
569         //   final Set<Integer> to_delete = new HashSet<Integer>();
570         for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
571             final PhylogenyNode n = it.next();
572             if ( n.getNodeData().isHasTaxonomy() ) {
573                 if ( !species_to_keep.contains( n.getNodeData().getTaxonomy() ) ) {
574                     //to_delete.add( n.getNodeId() );
575                     phy.deleteSubtree( n, true );
576                 }
577             }
578             else {
579                 throw new IllegalArgumentException( "node " + n.getId() + " has no taxonomic data" );
580             }
581         }
582         phy.hashIDs();
583         phy.externalNodesHaveChanged();
584         //  deleteExternalNodesNegativeSelection( to_delete, phy );
585     }
586
587     public static List<String> deleteExternalNodesPositiveSelection( final String[] node_names_to_keep,
588                                                                      final Phylogeny p ) {
589         final PhylogenyNodeIterator it = p.iteratorExternalForward();
590         final String[] to_delete = new String[ p.getNumberOfExternalNodes() ];
591         int i = 0;
592         Arrays.sort( node_names_to_keep );
593         while ( it.hasNext() ) {
594             final String curent_name = it.next().getName();
595             if ( Arrays.binarySearch( node_names_to_keep, curent_name ) < 0 ) {
596                 to_delete[ i++ ] = curent_name;
597             }
598         }
599         PhylogenyMethods.deleteExternalNodesNegativeSelection( to_delete, p );
600         final List<String> deleted = new ArrayList<String>();
601         for( final String n : to_delete ) {
602             if ( !ForesterUtil.isEmpty( n ) ) {
603                 deleted.add( n );
604             }
605         }
606         return deleted;
607     }
608
609     public static List<PhylogenyNode> getAllDescendants( final PhylogenyNode node ) {
610         final List<PhylogenyNode> descs = new ArrayList<PhylogenyNode>();
611         final Set<Integer> encountered = new HashSet<Integer>();
612         if ( !node.isExternal() ) {
613             final List<PhylogenyNode> exts = node.getAllExternalDescendants();
614             for( PhylogenyNode current : exts ) {
615                 descs.add( current );
616                 while ( current != node ) {
617                     current = current.getParent();
618                     if ( encountered.contains( current.getId() ) ) {
619                         continue;
620                     }
621                     descs.add( current );
622                     encountered.add( current.getId() );
623                 }
624             }
625         }
626         return descs;
627     }
628
629     /**
630      * 
631      * Convenience method
632      * 
633      * @param node
634      * @return
635      */
636     public static Color getBranchColorValue( final PhylogenyNode node ) {
637         if ( node.getBranchData().getBranchColor() == null ) {
638             return null;
639         }
640         return node.getBranchData().getBranchColor().getValue();
641     }
642
643     /**
644      * Convenience method
645      */
646     public static double getBranchWidthValue( final PhylogenyNode node ) {
647         if ( !node.getBranchData().isHasBranchWidth() ) {
648             return BranchWidth.BRANCH_WIDTH_DEFAULT_VALUE;
649         }
650         return node.getBranchData().getBranchWidth().getValue();
651     }
652
653     /**
654      * Convenience method
655      */
656     public static double getConfidenceValue( final PhylogenyNode node ) {
657         if ( !node.getBranchData().isHasConfidences() ) {
658             return Confidence.CONFIDENCE_DEFAULT_VALUE;
659         }
660         return node.getBranchData().getConfidence( 0 ).getValue();
661     }
662
663     /**
664      * Convenience method
665      */
666     public static double[] getConfidenceValuesAsArray( final PhylogenyNode node ) {
667         if ( !node.getBranchData().isHasConfidences() ) {
668             return new double[ 0 ];
669         }
670         final double[] values = new double[ node.getBranchData().getConfidences().size() ];
671         int i = 0;
672         for( final Confidence c : node.getBranchData().getConfidences() ) {
673             values[ i++ ] = c.getValue();
674         }
675         return values;
676     }
677
678     /**
679      * Calculates the distance between PhylogenyNodes n1 and n2.
680      * PRECONDITION: n1 is a descendant of n2.
681      * 
682      * @param n1
683      *            a descendant of n2
684      * @param n2
685      * @return distance between n1 and n2
686      */
687     private static double getDistance( PhylogenyNode n1, final PhylogenyNode n2 ) {
688         double d = 0.0;
689         while ( n1 != n2 ) {
690             if ( n1.getDistanceToParent() > 0.0 ) {
691                 d += n1.getDistanceToParent();
692             }
693             n1 = n1.getParent();
694         }
695         return d;
696     }
697
698     /**
699      * Returns taxonomy t if all external descendants have 
700      * the same taxonomy t, null otherwise.
701      * 
702      */
703     public static Taxonomy getExternalDescendantsTaxonomy( final PhylogenyNode node ) {
704         final List<PhylogenyNode> descs = node.getAllExternalDescendants();
705         Taxonomy tax = null;
706         for( final PhylogenyNode n : descs ) {
707             if ( !n.getNodeData().isHasTaxonomy() || n.getNodeData().getTaxonomy().isEmpty() ) {
708                 return null;
709             }
710             else if ( tax == null ) {
711                 tax = n.getNodeData().getTaxonomy();
712             }
713             else if ( n.getNodeData().getTaxonomy().isEmpty() || !tax.isEqual( n.getNodeData().getTaxonomy() ) ) {
714                 return null;
715             }
716         }
717         return tax;
718     }
719
720     public static PhylogenyNode getFurthestDescendant( final PhylogenyNode node ) {
721         final List<PhylogenyNode> children = node.getAllExternalDescendants();
722         PhylogenyNode farthest = null;
723         double longest = -Double.MAX_VALUE;
724         for( final PhylogenyNode child : children ) {
725             if ( PhylogenyMethods.getDistance( child, node ) > longest ) {
726                 farthest = child;
727                 longest = PhylogenyMethods.getDistance( child, node );
728             }
729         }
730         return farthest;
731     }
732
733     public static PhylogenyMethods getInstance() {
734         if ( PhylogenyMethods._instance == null ) {
735             PhylogenyMethods._instance = new PhylogenyMethods();
736         }
737         return PhylogenyMethods._instance;
738     }
739
740     /**
741      * Returns the largest confidence value found on phy.
742      */
743     static public double getMaximumConfidenceValue( final Phylogeny phy ) {
744         double max = -Double.MAX_VALUE;
745         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {
746             final double s = PhylogenyMethods.getConfidenceValue( iter.next() );
747             if ( ( s != Confidence.CONFIDENCE_DEFAULT_VALUE ) && ( s > max ) ) {
748                 max = s;
749             }
750         }
751         return max;
752     }
753
754     static public int getMinimumDescendentsPerInternalNodes( final Phylogeny phy ) {
755         int min = Integer.MAX_VALUE;
756         int d = 0;
757         PhylogenyNode n;
758         for( final PhylogenyNodeIterator it = phy.iteratorPreorder(); it.hasNext(); ) {
759             n = it.next();
760             if ( n.isInternal() ) {
761                 d = n.getNumberOfDescendants();
762                 if ( d < min ) {
763                     min = d;
764                 }
765             }
766         }
767         return min;
768     }
769
770     /**
771      * Convenience method for display purposes.
772      * Not intended for algorithms.
773      */
774     public static String getSpecies( final PhylogenyNode node ) {
775         if ( !node.getNodeData().isHasTaxonomy() ) {
776             return "";
777         }
778         if ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
779             return node.getNodeData().getTaxonomy().getTaxonomyCode();
780         }
781         else if ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getScientificName() ) ) {
782             return node.getNodeData().getTaxonomy().getScientificName();
783         }
784         else {
785             return node.getNodeData().getTaxonomy().getCommonName();
786         }
787     }
788
789     /**
790      * Returns all Nodes which are connected to external PhylogenyNode n of this
791      * Phylogeny by a path containing only speciation events. We call these
792      * "super orthologs". Nodes are returned as Vector of references to Nodes.
793      * <p>
794      * PRECONDITION: This tree must be binary and rooted, and speciation -
795      * duplication need to be assigned for each of its internal Nodes.
796      * <p>
797      * Returns null if this Phylogeny is empty or if n is internal.
798      * @param n
799      *            external PhylogenyNode whose strictly speciation related Nodes
800      *            are to be returned
801      * @return Vector of references to all strictly speciation related Nodes of
802      *         PhylogenyNode n of this Phylogeny, null if this Phylogeny is
803      *         empty or if n is internal
804      */
805     public static List<PhylogenyNode> getSuperOrthologousNodes( final PhylogenyNode n ) {
806         // FIXME
807         PhylogenyNode node = n, deepest = null;
808         final List<PhylogenyNode> v = new ArrayList<PhylogenyNode>();
809         if ( !node.isExternal() ) {
810             return null;
811         }
812         while ( !node.isRoot() && !node.getParent().isDuplication() ) {
813             node = node.getParent();
814         }
815         deepest = node;
816         deepest.setIndicatorsToZero();
817         do {
818             if ( !node.isExternal() ) {
819                 if ( node.getIndicator() == 0 ) {
820                     node.setIndicator( ( byte ) 1 );
821                     if ( !node.isDuplication() ) {
822                         node = node.getChildNode1();
823                     }
824                 }
825                 if ( node.getIndicator() == 1 ) {
826                     node.setIndicator( ( byte ) 2 );
827                     if ( !node.isDuplication() ) {
828                         node = node.getChildNode2();
829                     }
830                 }
831                 if ( ( node != deepest ) && ( node.getIndicator() == 2 ) ) {
832                     node = node.getParent();
833                 }
834             }
835             else {
836                 if ( node != n ) {
837                     v.add( node );
838                 }
839                 if ( node != deepest ) {
840                     node = node.getParent();
841                 }
842                 else {
843                     node.setIndicator( ( byte ) 2 );
844                 }
845             }
846         } while ( ( node != deepest ) || ( deepest.getIndicator() != 2 ) );
847         return v;
848     }
849
850     /**
851      * Convenience method for display purposes.
852      * Not intended for algorithms.
853      */
854     public static String getTaxonomyIdentifier( final PhylogenyNode node ) {
855         if ( !node.getNodeData().isHasTaxonomy() || ( node.getNodeData().getTaxonomy().getIdentifier() == null ) ) {
856             return "";
857         }
858         return node.getNodeData().getTaxonomy().getIdentifier().getValue();
859     }
860
861     /**
862      * Returns all Nodes which are connected to external PhylogenyNode n of this
863      * Phylogeny by a path containing, and leading to, only duplication events.
864      * We call these "ultra paralogs". Nodes are returned as Vector of
865      * references to Nodes.
866      * <p>
867      * PRECONDITION: This tree must be binary and rooted, and speciation -
868      * duplication need to be assigned for each of its internal Nodes.
869      * <p>
870      * Returns null if this Phylogeny is empty or if n is internal.
871      * <p>
872      * (Last modified: 10/06/01)
873      * 
874      * @param n
875      *            external PhylogenyNode whose ultra paralogs are to be returned
876      * @return Vector of references to all ultra paralogs of PhylogenyNode n of
877      *         this Phylogeny, null if this Phylogeny is empty or if n is
878      *         internal
879      */
880     public static List<PhylogenyNode> getUltraParalogousNodes( final PhylogenyNode n ) {
881         // FIXME test me
882         PhylogenyNode node = n;
883         if ( !node.isExternal() ) {
884             return null;
885         }
886         while ( !node.isRoot() && node.getParent().isDuplication() && areAllChildrenDuplications( node.getParent() ) ) {
887             node = node.getParent();
888         }
889         final List<PhylogenyNode> nodes = node.getAllExternalDescendants();
890         nodes.remove( n );
891         return nodes;
892     }
893
894     public static String inferCommonPartOfScientificNameOfDescendants( final PhylogenyNode node ) {
895         final List<PhylogenyNode> descs = node.getDescendants();
896         String sn = null;
897         for( final PhylogenyNode n : descs ) {
898             if ( !n.getNodeData().isHasTaxonomy()
899                     || ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
900                 return null;
901             }
902             else if ( sn == null ) {
903                 sn = n.getNodeData().getTaxonomy().getScientificName().trim();
904             }
905             else {
906                 String sn_current = n.getNodeData().getTaxonomy().getScientificName().trim();
907                 if ( !sn.equals( sn_current ) ) {
908                     boolean overlap = false;
909                     while ( ( sn.indexOf( ' ' ) >= 0 ) || ( sn_current.indexOf( ' ' ) >= 0 ) ) {
910                         if ( ForesterUtil.countChars( sn, ' ' ) > ForesterUtil.countChars( sn_current, ' ' ) ) {
911                             sn = sn.substring( 0, sn.lastIndexOf( ' ' ) ).trim();
912                         }
913                         else {
914                             sn_current = sn_current.substring( 0, sn_current.lastIndexOf( ' ' ) ).trim();
915                         }
916                         if ( sn.equals( sn_current ) ) {
917                             overlap = true;
918                             break;
919                         }
920                     }
921                     if ( !overlap ) {
922                         return null;
923                     }
924                 }
925             }
926         }
927         return sn;
928     }
929
930     public static boolean isHasExternalDescendant( final PhylogenyNode node ) {
931         for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
932             if ( node.getChildNode( i ).isExternal() ) {
933                 return true;
934             }
935         }
936         return false;
937     }
938
939     /*
940      * This is case insensitive.
941      * 
942      */
943     public synchronized static boolean isTaxonomyHasIdentifierOfGivenProvider( final Taxonomy tax,
944                                                                                final String[] providers ) {
945         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() ) ) {
946             final String my_tax_prov = tax.getIdentifier().getProvider();
947             for( final String provider : providers ) {
948                 if ( provider.equalsIgnoreCase( my_tax_prov ) ) {
949                     return true;
950                 }
951             }
952             return false;
953         }
954         else {
955             return false;
956         }
957     }
958
959     private static boolean match( final String s,
960                                   final String query,
961                                   final boolean case_sensitive,
962                                   final boolean partial ) {
963         if ( ForesterUtil.isEmpty( s ) || ForesterUtil.isEmpty( query ) ) {
964             return false;
965         }
966         String my_s = s.trim();
967         String my_query = query.trim();
968         if ( !case_sensitive ) {
969             my_s = my_s.toLowerCase();
970             my_query = my_query.toLowerCase();
971         }
972         if ( partial ) {
973             return my_s.indexOf( my_query ) >= 0;
974         }
975         else {
976             return my_s.equals( my_query );
977         }
978     }
979
980     public static void midpointRoot( final Phylogeny phylogeny ) {
981         if ( phylogeny.getNumberOfExternalNodes() < 2 ) {
982             return;
983         }
984         final PhylogenyMethods methods = getInstance();
985         final double farthest_d = methods.calculateFurthestDistance( phylogeny );
986         final PhylogenyNode f1 = methods.getFarthestNode1();
987         final PhylogenyNode f2 = methods.getFarthestNode2();
988         if ( farthest_d <= 0.0 ) {
989             return;
990         }
991         double x = farthest_d / 2.0;
992         PhylogenyNode n = f1;
993         if ( PhylogenyMethods.getDistance( f1, phylogeny.getRoot() ) < PhylogenyMethods.getDistance( f2, phylogeny
994                 .getRoot() ) ) {
995             n = f2;
996         }
997         while ( ( x > n.getDistanceToParent() ) && !n.isRoot() ) {
998             x -= ( n.getDistanceToParent() > 0 ? n.getDistanceToParent() : 0 );
999             n = n.getParent();
1000         }
1001         phylogeny.reRoot( n, x );
1002         phylogeny.recalculateNumberOfExternalDescendants( true );
1003         final PhylogenyNode a = getFurthestDescendant( phylogeny.getRoot().getChildNode1() );
1004         final PhylogenyNode b = getFurthestDescendant( phylogeny.getRoot().getChildNode2() );
1005         final double da = getDistance( a, phylogeny.getRoot() );
1006         final double db = getDistance( b, phylogeny.getRoot() );
1007         if ( Math.abs( da - db ) > 0.000001 ) {
1008             throw new FailedConditionCheckException( "this should not have happened: midpoint rooting failed:  da="
1009                     + da + ",  db=" + db + ",  diff=" + Math.abs( da - db ) );
1010         }
1011     }
1012
1013     public static void normalizeBootstrapValues( final Phylogeny phylogeny,
1014                                                  final double max_bootstrap_value,
1015                                                  final double max_normalized_value ) {
1016         for( final PhylogenyNodeIterator iter = phylogeny.iteratorPreorder(); iter.hasNext(); ) {
1017             final PhylogenyNode node = iter.next();
1018             if ( node.isInternal() ) {
1019                 final double confidence = getConfidenceValue( node );
1020                 if ( confidence != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
1021                     if ( confidence >= max_bootstrap_value ) {
1022                         setBootstrapConfidence( node, max_normalized_value );
1023                     }
1024                     else {
1025                         setBootstrapConfidence( node, ( confidence * max_normalized_value ) / max_bootstrap_value );
1026                     }
1027                 }
1028             }
1029         }
1030     }
1031
1032     public static List<PhylogenyNode> obtainAllNodesAsList( final Phylogeny phy ) {
1033         final List<PhylogenyNode> nodes = new ArrayList<PhylogenyNode>();
1034         if ( phy.isEmpty() ) {
1035             return nodes;
1036         }
1037         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {
1038             nodes.add( iter.next() );
1039         }
1040         return nodes;
1041     }
1042
1043     public static void postorderBranchColorAveragingExternalNodeBased( final Phylogeny p ) {
1044         for( final PhylogenyNodeIterator iter = p.iteratorPostorder(); iter.hasNext(); ) {
1045             final PhylogenyNode node = iter.next();
1046             double red = 0.0;
1047             double green = 0.0;
1048             double blue = 0.0;
1049             int n = 0;
1050             if ( node.isInternal() ) {
1051                 for( final PhylogenyNodeIterator iterator = node.iterateChildNodesForward(); iterator.hasNext(); ) {
1052                     final PhylogenyNode child_node = iterator.next();
1053                     final Color child_color = getBranchColorValue( child_node );
1054                     if ( child_color != null ) {
1055                         ++n;
1056                         red += child_color.getRed();
1057                         green += child_color.getGreen();
1058                         blue += child_color.getBlue();
1059                     }
1060                 }
1061                 setBranchColorValue( node,
1062                                      new Color( ForesterUtil.roundToInt( red / n ),
1063                                                 ForesterUtil.roundToInt( green / n ),
1064                                                 ForesterUtil.roundToInt( blue / n ) ) );
1065             }
1066         }
1067     }
1068
1069     public static void removeNode( final PhylogenyNode remove_me, final Phylogeny phylogeny ) {
1070         if ( remove_me.isRoot() ) {
1071             throw new IllegalArgumentException( "ill advised attempt to remove root node" );
1072         }
1073         if ( remove_me.isExternal() ) {
1074             phylogeny.deleteSubtree( remove_me, false );
1075         }
1076         else {
1077             final PhylogenyNode parent = remove_me.getParent();
1078             final List<PhylogenyNode> descs = remove_me.getDescendants();
1079             parent.removeChildNode( remove_me );
1080             for( final PhylogenyNode desc : descs ) {
1081                 parent.addAsChild( desc );
1082                 desc.setDistanceToParent( addPhylogenyDistances( remove_me.getDistanceToParent(),
1083                                                                  desc.getDistanceToParent() ) );
1084             }
1085             remove_me.setParent( null );
1086             phylogeny.setIdHash( null );
1087             phylogeny.externalNodesHaveChanged();
1088         }
1089     }
1090
1091     public static List<PhylogenyNode> searchData( final String query,
1092                                                   final Phylogeny phy,
1093                                                   final boolean case_sensitive,
1094                                                   final boolean partial ) {
1095         final List<PhylogenyNode> nodes = new ArrayList<PhylogenyNode>();
1096         if ( phy.isEmpty() || ( query == null ) ) {
1097             return nodes;
1098         }
1099         if ( ForesterUtil.isEmpty( query ) ) {
1100             return nodes;
1101         }
1102         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {
1103             final PhylogenyNode node = iter.next();
1104             boolean match = false;
1105             if ( match( node.getName(), query, case_sensitive, partial ) ) {
1106                 match = true;
1107             }
1108             else if ( node.getNodeData().isHasTaxonomy()
1109                     && match( node.getNodeData().getTaxonomy().getTaxonomyCode(), query, case_sensitive, partial ) ) {
1110                 match = true;
1111             }
1112             else if ( node.getNodeData().isHasTaxonomy()
1113                     && match( node.getNodeData().getTaxonomy().getCommonName(), query, case_sensitive, partial ) ) {
1114                 match = true;
1115             }
1116             else if ( node.getNodeData().isHasTaxonomy()
1117                     && match( node.getNodeData().getTaxonomy().getScientificName(), query, case_sensitive, partial ) ) {
1118                 match = true;
1119             }
1120             else if ( node.getNodeData().isHasTaxonomy()
1121                     && ( node.getNodeData().getTaxonomy().getIdentifier() != null )
1122                     && match( node.getNodeData().getTaxonomy().getIdentifier().getValue(),
1123                               query,
1124                               case_sensitive,
1125                               partial ) ) {
1126                 match = true;
1127             }
1128             else if ( node.getNodeData().isHasTaxonomy() && !node.getNodeData().getTaxonomy().getSynonyms().isEmpty() ) {
1129                 final List<String> syns = node.getNodeData().getTaxonomy().getSynonyms();
1130                 I: for( final String syn : syns ) {
1131                     if ( match( syn, query, case_sensitive, partial ) ) {
1132                         match = true;
1133                         break I;
1134                     }
1135                 }
1136             }
1137             if ( !match && node.getNodeData().isHasSequence()
1138                     && match( node.getNodeData().getSequence().getName(), query, case_sensitive, partial ) ) {
1139                 match = true;
1140             }
1141             if ( !match && node.getNodeData().isHasSequence()
1142                     && match( node.getNodeData().getSequence().getSymbol(), query, case_sensitive, partial ) ) {
1143                 match = true;
1144             }
1145             if ( !match
1146                     && node.getNodeData().isHasSequence()
1147                     && ( node.getNodeData().getSequence().getAccession() != null )
1148                     && match( node.getNodeData().getSequence().getAccession().getValue(),
1149                               query,
1150                               case_sensitive,
1151                               partial ) ) {
1152                 match = true;
1153             }
1154             if ( !match && node.getNodeData().isHasSequence()
1155                     && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
1156                 final DomainArchitecture da = node.getNodeData().getSequence().getDomainArchitecture();
1157                 I: for( int i = 0; i < da.getNumberOfDomains(); ++i ) {
1158                     if ( match( da.getDomain( i ).getName(), query, case_sensitive, partial ) ) {
1159                         match = true;
1160                         break I;
1161                     }
1162                 }
1163             }
1164             if ( !match && ( node.getNodeData().getBinaryCharacters() != null ) ) {
1165                 Iterator<String> it = node.getNodeData().getBinaryCharacters().getPresentCharacters().iterator();
1166                 I: while ( it.hasNext() ) {
1167                     if ( match( it.next(), query, case_sensitive, partial ) ) {
1168                         match = true;
1169                         break I;
1170                     }
1171                 }
1172                 it = node.getNodeData().getBinaryCharacters().getGainedCharacters().iterator();
1173                 I: while ( it.hasNext() ) {
1174                     if ( match( it.next(), query, case_sensitive, partial ) ) {
1175                         match = true;
1176                         break I;
1177                     }
1178                 }
1179             }
1180             if ( match ) {
1181                 nodes.add( node );
1182             }
1183         }
1184         return nodes;
1185     }
1186
1187     public static List<PhylogenyNode> searchDataLogicalAnd( final String[] queries,
1188                                                             final Phylogeny phy,
1189                                                             final boolean case_sensitive,
1190                                                             final boolean partial ) {
1191         final List<PhylogenyNode> nodes = new ArrayList<PhylogenyNode>();
1192         if ( phy.isEmpty() || ( queries == null ) || ( queries.length < 1 ) ) {
1193             return nodes;
1194         }
1195         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {
1196             final PhylogenyNode node = iter.next();
1197             boolean all_matched = true;
1198             for( final String query : queries ) {
1199                 boolean match = false;
1200                 if ( ForesterUtil.isEmpty( query ) ) {
1201                     continue;
1202                 }
1203                 if ( match( node.getName(), query, case_sensitive, partial ) ) {
1204                     match = true;
1205                 }
1206                 else if ( node.getNodeData().isHasTaxonomy()
1207                         && match( node.getNodeData().getTaxonomy().getTaxonomyCode(), query, case_sensitive, partial ) ) {
1208                     match = true;
1209                 }
1210                 else if ( node.getNodeData().isHasTaxonomy()
1211                         && match( node.getNodeData().getTaxonomy().getCommonName(), query, case_sensitive, partial ) ) {
1212                     match = true;
1213                 }
1214                 else if ( node.getNodeData().isHasTaxonomy()
1215                         && match( node.getNodeData().getTaxonomy().getScientificName(), query, case_sensitive, partial ) ) {
1216                     match = true;
1217                 }
1218                 else if ( node.getNodeData().isHasTaxonomy()
1219                         && ( node.getNodeData().getTaxonomy().getIdentifier() != null )
1220                         && match( node.getNodeData().getTaxonomy().getIdentifier().getValue(),
1221                                   query,
1222                                   case_sensitive,
1223                                   partial ) ) {
1224                     match = true;
1225                 }
1226                 else if ( node.getNodeData().isHasTaxonomy()
1227                         && !node.getNodeData().getTaxonomy().getSynonyms().isEmpty() ) {
1228                     final List<String> syns = node.getNodeData().getTaxonomy().getSynonyms();
1229                     I: for( final String syn : syns ) {
1230                         if ( match( syn, query, case_sensitive, partial ) ) {
1231                             match = true;
1232                             break I;
1233                         }
1234                     }
1235                 }
1236                 if ( !match && node.getNodeData().isHasSequence()
1237                         && match( node.getNodeData().getSequence().getName(), query, case_sensitive, partial ) ) {
1238                     match = true;
1239                 }
1240                 if ( !match && node.getNodeData().isHasSequence()
1241                         && match( node.getNodeData().getSequence().getSymbol(), query, case_sensitive, partial ) ) {
1242                     match = true;
1243                 }
1244                 if ( !match
1245                         && node.getNodeData().isHasSequence()
1246                         && ( node.getNodeData().getSequence().getAccession() != null )
1247                         && match( node.getNodeData().getSequence().getAccession().getValue(),
1248                                   query,
1249                                   case_sensitive,
1250                                   partial ) ) {
1251                     match = true;
1252                 }
1253                 if ( !match && node.getNodeData().isHasSequence()
1254                         && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
1255                     final DomainArchitecture da = node.getNodeData().getSequence().getDomainArchitecture();
1256                     I: for( int i = 0; i < da.getNumberOfDomains(); ++i ) {
1257                         if ( match( da.getDomain( i ).getName(), query, case_sensitive, partial ) ) {
1258                             match = true;
1259                             break I;
1260                         }
1261                     }
1262                 }
1263                 if ( !match && ( node.getNodeData().getBinaryCharacters() != null ) ) {
1264                     Iterator<String> it = node.getNodeData().getBinaryCharacters().getPresentCharacters().iterator();
1265                     I: while ( it.hasNext() ) {
1266                         if ( match( it.next(), query, case_sensitive, partial ) ) {
1267                             match = true;
1268                             break I;
1269                         }
1270                     }
1271                     it = node.getNodeData().getBinaryCharacters().getGainedCharacters().iterator();
1272                     I: while ( it.hasNext() ) {
1273                         if ( match( it.next(), query, case_sensitive, partial ) ) {
1274                             match = true;
1275                             break I;
1276                         }
1277                     }
1278                     //                    final String[] bcp_ary = node.getNodeData().getBinaryCharacters()
1279                     //                            .getPresentCharactersAsStringArray();
1280                     //                    I: for( final String bc : bcp_ary ) {
1281                     //                        if ( match( bc, query, case_sensitive, partial ) ) {
1282                     //                            match = true;
1283                     //                            break I;
1284                     //                        }
1285                     //                    }
1286                     //                    final String[] bcg_ary = node.getNodeData().getBinaryCharacters()
1287                     //                            .getGainedCharactersAsStringArray();
1288                     //                    I: for( final String bc : bcg_ary ) {
1289                     //                        if ( match( bc, query, case_sensitive, partial ) ) {
1290                     //                            match = true;
1291                     //                            break I;
1292                     //                        }
1293                     //                    }
1294                 }
1295                 if ( !match ) {
1296                     all_matched = false;
1297                     break;
1298                 }
1299             }
1300             if ( all_matched ) {
1301                 nodes.add( node );
1302             }
1303         }
1304         return nodes;
1305     }
1306
1307     /**
1308      * Convenience method.
1309      * Sets value for the first confidence value (created if not present, values overwritten otherwise). 
1310      */
1311     public static void setBootstrapConfidence( final PhylogenyNode node, final double bootstrap_confidence_value ) {
1312         setConfidence( node, bootstrap_confidence_value, "bootstrap" );
1313     }
1314
1315     public static void setBranchColorValue( final PhylogenyNode node, final Color color ) {
1316         if ( node.getBranchData().getBranchColor() == null ) {
1317             node.getBranchData().setBranchColor( new BranchColor() );
1318         }
1319         node.getBranchData().getBranchColor().setValue( color );
1320     }
1321
1322     /**
1323      * Convenience method
1324      */
1325     public static void setBranchWidthValue( final PhylogenyNode node, final double branch_width_value ) {
1326         node.getBranchData().setBranchWidth( new BranchWidth( branch_width_value ) );
1327     }
1328
1329     /**
1330      * Convenience method.
1331      * Sets value for the first confidence value (created if not present, values overwritten otherwise). 
1332      */
1333     public static void setConfidence( final PhylogenyNode node, final double confidence_value ) {
1334         setConfidence( node, confidence_value, "" );
1335     }
1336
1337     /**
1338      * Convenience method.
1339      * Sets value for the first confidence value (created if not present, values overwritten otherwise). 
1340      */
1341     public static void setConfidence( final PhylogenyNode node, final double confidence_value, final String type ) {
1342         Confidence c = null;
1343         if ( node.getBranchData().getNumberOfConfidences() > 0 ) {
1344             c = node.getBranchData().getConfidence( 0 );
1345         }
1346         else {
1347             c = new Confidence();
1348             node.getBranchData().addConfidence( c );
1349         }
1350         c.setType( type );
1351         c.setValue( confidence_value );
1352     }
1353
1354     public static void setScientificName( final PhylogenyNode node, final String scientific_name ) {
1355         if ( !node.getNodeData().isHasTaxonomy() ) {
1356             node.getNodeData().setTaxonomy( new Taxonomy() );
1357         }
1358         node.getNodeData().getTaxonomy().setScientificName( scientific_name );
1359     }
1360
1361     /**
1362      * Convenience method to set the taxonomy code of a phylogeny node.
1363      * 
1364      * 
1365      * @param node
1366      * @param taxonomy_code
1367      */
1368     public static void setTaxonomyCode( final PhylogenyNode node, final String taxonomy_code ) {
1369         if ( !node.getNodeData().isHasTaxonomy() ) {
1370             node.getNodeData().setTaxonomy( new Taxonomy() );
1371         }
1372         node.getNodeData().getTaxonomy().setTaxonomyCode( taxonomy_code );
1373     }
1374
1375     /**
1376      * Removes from Phylogeny to_be_stripped all external Nodes which are
1377      * associated with a species NOT found in Phylogeny reference.
1378      * 
1379      * @param reference
1380      *            a reference Phylogeny
1381      * @param to_be_stripped
1382      *            Phylogeny to be stripped
1383      * @return number of external nodes removed from to_be_stripped
1384      */
1385     public static int taxonomyBasedDeletionOfExternalNodes( final Phylogeny reference, final Phylogeny to_be_stripped ) {
1386         final Set<String> ref_ext_taxo = new HashSet<String>();
1387         final ArrayList<PhylogenyNode> nodes_to_delete = new ArrayList<PhylogenyNode>();
1388         for( final PhylogenyNodeIterator it = reference.iteratorExternalForward(); it.hasNext(); ) {
1389             ref_ext_taxo.add( getSpecies( it.next() ) );
1390         }
1391         for( final PhylogenyNodeIterator it = to_be_stripped.iteratorExternalForward(); it.hasNext(); ) {
1392             final PhylogenyNode n = it.next();
1393             if ( !ref_ext_taxo.contains( getSpecies( n ) ) ) {
1394                 nodes_to_delete.add( n );
1395             }
1396         }
1397         for( final PhylogenyNode phylogenyNode : nodes_to_delete ) {
1398             to_be_stripped.deleteSubtree( phylogenyNode, true );
1399         }
1400         return nodes_to_delete.size();
1401     }
1402
1403     public static enum PhylogenyNodeField {
1404         CLADE_NAME,
1405         TAXONOMY_CODE,
1406         TAXONOMY_SCIENTIFIC_NAME,
1407         TAXONOMY_COMMON_NAME,
1408         SEQUENCE_SYMBOL,
1409         SEQUENCE_NAME,
1410         TAXONOMY_ID_UNIPROT_1,
1411         TAXONOMY_ID_UNIPROT_2;
1412     }
1413
1414     public static enum TAXONOMY_EXTRACTION {
1415         NO, YES, PFAM_STYLE_ONLY;
1416     }
1417 }