inprogress
[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: https://sites.google.com/site/cmzmasek/home/software/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.Collections;
34 import java.util.Comparator;
35 import java.util.HashMap;
36 import java.util.HashSet;
37 import java.util.Iterator;
38 import java.util.List;
39 import java.util.Set;
40 import java.util.SortedMap;
41 import java.util.TreeMap;
42
43 import org.forester.io.parsers.PhylogenyParser;
44 import org.forester.io.parsers.phyloxml.PhyloXmlDataFormatException;
45 import org.forester.io.parsers.phyloxml.PhyloXmlUtil;
46 import org.forester.io.parsers.util.PhylogenyParserException;
47 import org.forester.phylogeny.data.Accession;
48 import org.forester.phylogeny.data.Annotation;
49 import org.forester.phylogeny.data.BranchColor;
50 import org.forester.phylogeny.data.BranchWidth;
51 import org.forester.phylogeny.data.Confidence;
52 import org.forester.phylogeny.data.DomainArchitecture;
53 import org.forester.phylogeny.data.Event;
54 import org.forester.phylogeny.data.Identifier;
55 import org.forester.phylogeny.data.PhylogenyDataUtil;
56 import org.forester.phylogeny.data.Sequence;
57 import org.forester.phylogeny.data.Taxonomy;
58 import org.forester.phylogeny.factories.ParserBasedPhylogenyFactory;
59 import org.forester.phylogeny.factories.PhylogenyFactory;
60 import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
61 import org.forester.util.BasicDescriptiveStatistics;
62 import org.forester.util.DescriptiveStatistics;
63 import org.forester.util.ForesterUtil;
64
65 public class PhylogenyMethods {
66
67     private PhylogenyMethods() {
68         // Hidden constructor.
69     }
70
71     @Override
72     public Object clone() throws CloneNotSupportedException {
73         throw new CloneNotSupportedException();
74     }
75
76     public static DescriptiveStatistics calculatBranchLengthStatistics( final Phylogeny phy ) {
77         final DescriptiveStatistics stats = new BasicDescriptiveStatistics();
78         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {
79             final PhylogenyNode n = iter.next();
80             if ( !n.isRoot() && ( n.getDistanceToParent() >= 0.0 ) ) {
81                 stats.addValue( n.getDistanceToParent() );
82             }
83         }
84         return stats;
85     }
86
87     public static List<DescriptiveStatistics> calculatConfidenceStatistics( final Phylogeny phy ) {
88         final List<DescriptiveStatistics> stats = new ArrayList<DescriptiveStatistics>();
89         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {
90             final PhylogenyNode n = iter.next();
91             if ( !n.isExternal() && !n.isRoot() ) {
92                 if ( n.getBranchData().isHasConfidences() ) {
93                     for( int i = 0; i < n.getBranchData().getConfidences().size(); ++i ) {
94                         final Confidence c = n.getBranchData().getConfidences().get( i );
95                         if ( ( i > ( stats.size() - 1 ) ) || ( stats.get( i ) == null ) ) {
96                             stats.add( i, new BasicDescriptiveStatistics() );
97                         }
98                         if ( !ForesterUtil.isEmpty( c.getType() ) ) {
99                             if ( !ForesterUtil.isEmpty( stats.get( i ).getDescription() ) ) {
100                                 if ( !stats.get( i ).getDescription().equalsIgnoreCase( c.getType() ) ) {
101                                     throw new IllegalArgumentException( "support values in node [" + n.toString()
102                                             + "] appear inconsistently ordered" );
103                                 }
104                             }
105                             stats.get( i ).setDescription( c.getType() );
106                         }
107                         stats.get( i ).addValue( ( ( c != null ) && ( c.getValue() >= 0 ) ) ? c.getValue() : 0 );
108                     }
109                 }
110             }
111         }
112         return stats;
113     }
114
115     /**
116      * Calculates the distance between PhylogenyNodes node1 and node2.
117      * 
118      * 
119      * @param node1
120      * @param node2
121      * @return distance between node1 and node2
122      */
123     public static double calculateDistance( final PhylogenyNode node1, final PhylogenyNode node2 ) {
124         final PhylogenyNode lca = calculateLCA( node1, node2 );
125         final PhylogenyNode n1 = node1;
126         final PhylogenyNode n2 = node2;
127         return ( PhylogenyMethods.getDistance( n1, lca ) + PhylogenyMethods.getDistance( n2, lca ) );
128     }
129
130     /**
131      * Returns the LCA of PhylogenyNodes node1 and node2.
132      * 
133      * 
134      * @param node1
135      * @param node2
136      * @return LCA of node1 and node2
137      */
138     public final static PhylogenyNode calculateLCA( PhylogenyNode node1, PhylogenyNode node2 ) {
139         if ( node1 == null ) {
140             throw new IllegalArgumentException( "first argument (node) is null" );
141         }
142         if ( node2 == null ) {
143             throw new IllegalArgumentException( "second argument (node) is null" );
144         }
145         if ( node1 == node2 ) {
146             return node1;
147         }
148         if ( ( node1.getParent() == node2.getParent() ) ) {
149             return node1.getParent();
150         }
151         int depth1 = node1.calculateDepth();
152         int depth2 = node2.calculateDepth();
153         while ( ( depth1 > -1 ) && ( depth2 > -1 ) ) {
154             if ( depth1 > depth2 ) {
155                 node1 = node1.getParent();
156                 depth1--;
157             }
158             else if ( depth2 > depth1 ) {
159                 node2 = node2.getParent();
160                 depth2--;
161             }
162             else {
163                 if ( node1 == node2 ) {
164                     return node1;
165                 }
166                 node1 = node1.getParent();
167                 node2 = node2.getParent();
168                 depth1--;
169                 depth2--;
170             }
171         }
172         throw new IllegalArgumentException( "illegal attempt to calculate LCA of two nodes which do not share a common root" );
173     }
174
175     /**
176      * Returns the LCA of PhylogenyNodes node1 and node2.
177      * Precondition: ids are in pre-order (or level-order).
178      * 
179      * 
180      * @param node1
181      * @param node2
182      * @return LCA of node1 and node2
183      */
184     public final static PhylogenyNode calculateLCAonTreeWithIdsInPreOrder( PhylogenyNode node1, PhylogenyNode node2 ) {
185         if ( node1 == null ) {
186             throw new IllegalArgumentException( "first argument (node) is null" );
187         }
188         if ( node2 == null ) {
189             throw new IllegalArgumentException( "second argument (node) is null" );
190         }
191         while ( node1 != node2 ) {
192             if ( node1.getId() > node2.getId() ) {
193                 node1 = node1.getParent();
194             }
195             else {
196                 node2 = node2.getParent();
197             }
198         }
199         return node1;
200     }
201
202     public static short calculateMaxBranchesToLeaf( final PhylogenyNode node ) {
203         if ( node.isExternal() ) {
204             return 0;
205         }
206         short max = 0;
207         for( PhylogenyNode d : node.getAllExternalDescendants() ) {
208             short steps = 0;
209             while ( d != node ) {
210                 if ( d.isCollapse() ) {
211                     steps = 0;
212                 }
213                 else {
214                     steps++;
215                 }
216                 d = d.getParent();
217             }
218             if ( max < steps ) {
219                 max = steps;
220             }
221         }
222         return max;
223     }
224
225     public static int calculateMaxDepth( final Phylogeny phy ) {
226         int max = 0;
227         for( final PhylogenyNodeIterator iter = phy.iteratorExternalForward(); iter.hasNext(); ) {
228             final PhylogenyNode node = iter.next();
229             final int steps = node.calculateDepth();
230             if ( steps > max ) {
231                 max = steps;
232             }
233         }
234         return max;
235     }
236
237     public static double calculateMaxDistanceToRoot( final Phylogeny phy ) {
238         double max = 0.0;
239         for( final PhylogenyNodeIterator iter = phy.iteratorExternalForward(); iter.hasNext(); ) {
240             final PhylogenyNode node = iter.next();
241             final double d = node.calculateDistanceToRoot();
242             if ( d > max ) {
243                 max = d;
244             }
245         }
246         return max;
247     }
248
249     public static int calculateNumberOfExternalNodesWithoutTaxonomy( final PhylogenyNode node ) {
250         final List<PhylogenyNode> descs = node.getAllExternalDescendants();
251         int x = 0;
252         for( final PhylogenyNode n : descs ) {
253             if ( !n.getNodeData().isHasTaxonomy() || n.getNodeData().getTaxonomy().isEmpty() ) {
254                 x++;
255             }
256         }
257         return x;
258     }
259
260     public static DescriptiveStatistics calculatNumberOfDescendantsPerNodeStatistics( final Phylogeny phy ) {
261         final DescriptiveStatistics stats = new BasicDescriptiveStatistics();
262         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {
263             final PhylogenyNode n = iter.next();
264             if ( !n.isExternal() ) {
265                 stats.addValue( n.getNumberOfDescendants() );
266             }
267         }
268         return stats;
269     }
270
271     public final static void collapseSubtreeStructure( final PhylogenyNode n ) {
272         final List<PhylogenyNode> eds = n.getAllExternalDescendants();
273         final List<Double> d = new ArrayList<Double>();
274         for( final PhylogenyNode ed : eds ) {
275             d.add( calculateDistanceToAncestor( n, ed ) );
276         }
277         for( int i = 0; i < eds.size(); ++i ) {
278             n.setChildNode( i, eds.get( i ) );
279             eds.get( i ).setDistanceToParent( d.get( i ) );
280         }
281     }
282
283     public static int countNumberOfOneDescendantNodes( final Phylogeny phy ) {
284         int count = 0;
285         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {
286             final PhylogenyNode n = iter.next();
287             if ( !n.isExternal() && ( n.getNumberOfDescendants() == 1 ) ) {
288                 count++;
289             }
290         }
291         return count;
292     }
293
294     public static int countNumberOfPolytomies( final Phylogeny phy ) {
295         int count = 0;
296         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {
297             final PhylogenyNode n = iter.next();
298             if ( !n.isExternal() && ( n.getNumberOfDescendants() > 2 ) ) {
299                 count++;
300             }
301         }
302         return count;
303     }
304
305     public static final HashMap<String, PhylogenyNode> createNameToExtNodeMap( final Phylogeny phy ) {
306         final HashMap<String, PhylogenyNode> nodes = new HashMap<String, PhylogenyNode>();
307         final List<PhylogenyNode> ext = phy.getExternalNodes();
308         for( final PhylogenyNode n : ext ) {
309             nodes.put( n.getName(), n );
310         }
311         return nodes;
312     }
313
314     public static void deleteExternalNodesNegativeSelection( final Set<Long> to_delete, final Phylogeny phy ) {
315         for( final Long id : to_delete ) {
316             phy.deleteSubtree( phy.getNode( id ), true );
317         }
318         phy.clearHashIdToNodeMap();
319         phy.externalNodesHaveChanged();
320     }
321
322     public static void deleteExternalNodesNegativeSelection( final String[] node_names_to_delete, final Phylogeny p )
323             throws IllegalArgumentException {
324         for( final String element : node_names_to_delete ) {
325             if ( ForesterUtil.isEmpty( element ) ) {
326                 continue;
327             }
328             List<PhylogenyNode> nodes = null;
329             nodes = p.getNodes( element );
330             final Iterator<PhylogenyNode> it = nodes.iterator();
331             while ( it.hasNext() ) {
332                 final PhylogenyNode n = it.next();
333                 if ( !n.isExternal() ) {
334                     throw new IllegalArgumentException( "attempt to delete non-external node \"" + element + "\"" );
335                 }
336                 p.deleteSubtree( n, true );
337             }
338         }
339         p.clearHashIdToNodeMap();
340         p.externalNodesHaveChanged();
341     }
342
343     public static List<String> deleteExternalNodesPositiveSelection( final String[] node_names_to_keep,
344                                                                      final Phylogeny p ) {
345         final PhylogenyNodeIterator it = p.iteratorExternalForward();
346         final String[] to_delete = new String[ p.getNumberOfExternalNodes() ];
347         int i = 0;
348         Arrays.sort( node_names_to_keep );
349         while ( it.hasNext() ) {
350             final String curent_name = it.next().getName();
351             if ( Arrays.binarySearch( node_names_to_keep, curent_name ) < 0 ) {
352                 to_delete[ i++ ] = curent_name;
353             }
354         }
355         PhylogenyMethods.deleteExternalNodesNegativeSelection( to_delete, p );
356         final List<String> deleted = new ArrayList<String>();
357         for( final String n : to_delete ) {
358             if ( !ForesterUtil.isEmpty( n ) ) {
359                 deleted.add( n );
360             }
361         }
362         return deleted;
363     }
364
365     public static void deleteExternalNodesPositiveSelectionT( final List<Taxonomy> species_to_keep, final Phylogeny phy ) {
366         final Set<Long> to_delete = new HashSet<Long>();
367         for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
368             final PhylogenyNode n = it.next();
369             if ( n.getNodeData().isHasTaxonomy() ) {
370                 if ( !species_to_keep.contains( n.getNodeData().getTaxonomy() ) ) {
371                     to_delete.add( n.getId() );
372                 }
373             }
374             else {
375                 throw new IllegalArgumentException( "node " + n.getId() + " has no taxonomic data" );
376             }
377         }
378         deleteExternalNodesNegativeSelection( to_delete, phy );
379     }
380
381     final public static void deleteInternalNodesWithOnlyOneDescendent( final Phylogeny phy ) {
382         final ArrayList<PhylogenyNode> to_delete = new ArrayList<PhylogenyNode>();
383         for( final PhylogenyNodeIterator iter = phy.iteratorPostorder(); iter.hasNext(); ) {
384             final PhylogenyNode n = iter.next();
385             if ( ( !n.isExternal() ) && ( n.getNumberOfDescendants() == 1 ) ) {
386                 to_delete.add( n );
387             }
388         }
389         for( final PhylogenyNode d : to_delete ) {
390             PhylogenyMethods.removeNode( d, phy );
391         }
392         phy.clearHashIdToNodeMap();
393         phy.externalNodesHaveChanged();
394     }
395
396     final public static void deleteNonOrthologousExternalNodes( final Phylogeny phy, final PhylogenyNode n ) {
397         if ( n.isInternal() ) {
398             throw new IllegalArgumentException( "node is not external" );
399         }
400         final ArrayList<PhylogenyNode> to_delete = new ArrayList<PhylogenyNode>();
401         for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
402             final PhylogenyNode i = it.next();
403             if ( !PhylogenyMethods.getEventAtLCA( n, i ).isSpeciation() ) {
404                 to_delete.add( i );
405             }
406         }
407         for( final PhylogenyNode d : to_delete ) {
408             phy.deleteSubtree( d, true );
409         }
410         phy.clearHashIdToNodeMap();
411         phy.externalNodesHaveChanged();
412     }
413
414     public static List<PhylogenyNode> getAllDescendants( final PhylogenyNode node ) {
415         final List<PhylogenyNode> descs = new ArrayList<PhylogenyNode>();
416         final Set<Long> encountered = new HashSet<Long>();
417         if ( !node.isExternal() ) {
418             final List<PhylogenyNode> exts = node.getAllExternalDescendants();
419             for( PhylogenyNode current : exts ) {
420                 descs.add( current );
421                 while ( current != node ) {
422                     current = current.getParent();
423                     if ( encountered.contains( current.getId() ) ) {
424                         continue;
425                     }
426                     descs.add( current );
427                     encountered.add( current.getId() );
428                 }
429             }
430         }
431         return descs;
432     }
433
434     /**
435      * 
436      * Convenience method
437      * 
438      * @param node
439      * @return
440      */
441     public static Color getBranchColorValue( final PhylogenyNode node ) {
442         if ( node.getBranchData().getBranchColor() == null ) {
443             return null;
444         }
445         return node.getBranchData().getBranchColor().getValue();
446     }
447
448     /**
449      * Convenience method
450      */
451     public static double getBranchWidthValue( final PhylogenyNode node ) {
452         if ( !node.getBranchData().isHasBranchWidth() ) {
453             return BranchWidth.BRANCH_WIDTH_DEFAULT_VALUE;
454         }
455         return node.getBranchData().getBranchWidth().getValue();
456     }
457
458     /**
459      * Convenience method
460      */
461     public static double getConfidenceValue( final PhylogenyNode node ) {
462         if ( !node.getBranchData().isHasConfidences() ) {
463             return Confidence.CONFIDENCE_DEFAULT_VALUE;
464         }
465         return node.getBranchData().getConfidence( 0 ).getValue();
466     }
467
468     /**
469      * Convenience method
470      */
471     public static double[] getConfidenceValuesAsArray( final PhylogenyNode node ) {
472         if ( !node.getBranchData().isHasConfidences() ) {
473             return new double[ 0 ];
474         }
475         final double[] values = new double[ node.getBranchData().getConfidences().size() ];
476         int i = 0;
477         for( final Confidence c : node.getBranchData().getConfidences() ) {
478             values[ i++ ] = c.getValue();
479         }
480         return values;
481     }
482
483     final public static Event getEventAtLCA( final PhylogenyNode n1, final PhylogenyNode n2 ) {
484         return calculateLCA( n1, n2 ).getNodeData().getEvent();
485     }
486
487     /**
488      * Returns taxonomy t if all external descendants have 
489      * the same taxonomy t, null otherwise.
490      * 
491      */
492     public static Taxonomy getExternalDescendantsTaxonomy( final PhylogenyNode node ) {
493         final List<PhylogenyNode> descs = node.getAllExternalDescendants();
494         Taxonomy tax = null;
495         for( final PhylogenyNode n : descs ) {
496             if ( !n.getNodeData().isHasTaxonomy() || n.getNodeData().getTaxonomy().isEmpty() ) {
497                 return null;
498             }
499             else if ( tax == null ) {
500                 tax = n.getNodeData().getTaxonomy();
501             }
502             else if ( n.getNodeData().getTaxonomy().isEmpty() || !tax.isEqual( n.getNodeData().getTaxonomy() ) ) {
503                 return null;
504             }
505         }
506         return tax;
507     }
508
509     public static PhylogenyNode getFurthestDescendant( final PhylogenyNode node ) {
510         final List<PhylogenyNode> children = node.getAllExternalDescendants();
511         PhylogenyNode farthest = null;
512         double longest = -Double.MAX_VALUE;
513         for( final PhylogenyNode child : children ) {
514             if ( PhylogenyMethods.getDistance( child, node ) > longest ) {
515                 farthest = child;
516                 longest = PhylogenyMethods.getDistance( child, node );
517             }
518         }
519         return farthest;
520     }
521
522     // public static PhylogenyMethods getInstance() {
523     //     if ( PhylogenyMethods._instance == null ) {
524     //         PhylogenyMethods._instance = new PhylogenyMethods();
525     //    }
526     //    return PhylogenyMethods._instance;
527     //  }
528     /**
529      * Returns the largest confidence value found on phy.
530      */
531     static public double getMaximumConfidenceValue( final Phylogeny phy ) {
532         double max = -Double.MAX_VALUE;
533         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {
534             final double s = PhylogenyMethods.getConfidenceValue( iter.next() );
535             if ( ( s != Confidence.CONFIDENCE_DEFAULT_VALUE ) && ( s > max ) ) {
536                 max = s;
537             }
538         }
539         return max;
540     }
541
542     static public int getMinimumDescendentsPerInternalNodes( final Phylogeny phy ) {
543         int min = Integer.MAX_VALUE;
544         int d = 0;
545         PhylogenyNode n;
546         for( final PhylogenyNodeIterator it = phy.iteratorPreorder(); it.hasNext(); ) {
547             n = it.next();
548             if ( n.isInternal() ) {
549                 d = n.getNumberOfDescendants();
550                 if ( d < min ) {
551                     min = d;
552                 }
553             }
554         }
555         return min;
556     }
557
558     /**
559      * Convenience method for display purposes.
560      * Not intended for algorithms.
561      */
562     public static String getSpecies( final PhylogenyNode node ) {
563         if ( !node.getNodeData().isHasTaxonomy() ) {
564             return "";
565         }
566         else if ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getScientificName() ) ) {
567             return node.getNodeData().getTaxonomy().getScientificName();
568         }
569         if ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
570             return node.getNodeData().getTaxonomy().getTaxonomyCode();
571         }
572         else {
573             return node.getNodeData().getTaxonomy().getCommonName();
574         }
575     }
576
577     /**
578      * Convenience method for display purposes.
579      * Not intended for algorithms.
580      */
581     public static String getTaxonomyIdentifier( final PhylogenyNode node ) {
582         if ( !node.getNodeData().isHasTaxonomy() || ( node.getNodeData().getTaxonomy().getIdentifier() == null ) ) {
583             return "";
584         }
585         return node.getNodeData().getTaxonomy().getIdentifier().getValue();
586     }
587
588     public final static boolean isAllDecendentsAreDuplications( final PhylogenyNode n ) {
589         if ( n.isExternal() ) {
590             return true;
591         }
592         else {
593             if ( n.isDuplication() ) {
594                 for( final PhylogenyNode desc : n.getDescendants() ) {
595                     if ( !isAllDecendentsAreDuplications( desc ) ) {
596                         return false;
597                     }
598                 }
599                 return true;
600             }
601             else {
602                 return false;
603             }
604         }
605     }
606
607     public static boolean isHasExternalDescendant( final PhylogenyNode node ) {
608         for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
609             if ( node.getChildNode( i ).isExternal() ) {
610                 return true;
611             }
612         }
613         return false;
614     }
615
616     /*
617      * This is case insensitive.
618      * 
619      */
620     public synchronized static boolean isTaxonomyHasIdentifierOfGivenProvider( final Taxonomy tax,
621                                                                                final String[] providers ) {
622         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() ) ) {
623             final String my_tax_prov = tax.getIdentifier().getProvider();
624             for( final String provider : providers ) {
625                 if ( provider.equalsIgnoreCase( my_tax_prov ) ) {
626                     return true;
627                 }
628             }
629             return false;
630         }
631         else {
632             return false;
633         }
634     }
635
636     public static void midpointRoot( final Phylogeny phylogeny ) {
637         if ( ( phylogeny.getNumberOfExternalNodes() < 2 ) || ( calculateMaxDistanceToRoot( phylogeny ) <= 0 ) ) {
638             return;
639         }
640         int counter = 0;
641         final int total_nodes = phylogeny.getNodeCount();
642         while ( true ) {
643             if ( ++counter > total_nodes ) {
644                 throw new RuntimeException( "this should not have happened: midpoint rooting does not converge" );
645             }
646             PhylogenyNode a = null;
647             double da = 0;
648             double db = 0;
649             for( int i = 0; i < phylogeny.getRoot().getNumberOfDescendants(); ++i ) {
650                 final PhylogenyNode f = getFurthestDescendant( phylogeny.getRoot().getChildNode( i ) );
651                 final double df = getDistance( f, phylogeny.getRoot() );
652                 if ( df > 0 ) {
653                     if ( df > da ) {
654                         db = da;
655                         da = df;
656                         a = f;
657                     }
658                     else if ( df > db ) {
659                         db = df;
660                     }
661                 }
662             }
663             final double diff = da - db;
664             if ( diff < 0.000001 ) {
665                 break;
666             }
667             double x = da - ( diff / 2.0 );
668             while ( ( x > a.getDistanceToParent() ) && !a.isRoot() ) {
669                 x -= ( a.getDistanceToParent() > 0 ? a.getDistanceToParent() : 0 );
670                 a = a.getParent();
671             }
672             phylogeny.reRoot( a, x );
673         }
674         phylogeny.recalculateNumberOfExternalDescendants( true );
675     }
676
677     public static void normalizeBootstrapValues( final Phylogeny phylogeny,
678                                                  final double max_bootstrap_value,
679                                                  final double max_normalized_value ) {
680         for( final PhylogenyNodeIterator iter = phylogeny.iteratorPreorder(); iter.hasNext(); ) {
681             final PhylogenyNode node = iter.next();
682             if ( node.isInternal() ) {
683                 final double confidence = getConfidenceValue( node );
684                 if ( confidence != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
685                     if ( confidence >= max_bootstrap_value ) {
686                         setBootstrapConfidence( node, max_normalized_value );
687                     }
688                     else {
689                         setBootstrapConfidence( node, ( confidence * max_normalized_value ) / max_bootstrap_value );
690                     }
691                 }
692             }
693         }
694     }
695
696     public static List<PhylogenyNode> obtainAllNodesAsList( final Phylogeny phy ) {
697         final List<PhylogenyNode> nodes = new ArrayList<PhylogenyNode>();
698         if ( phy.isEmpty() ) {
699             return nodes;
700         }
701         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {
702             nodes.add( iter.next() );
703         }
704         return nodes;
705     }
706
707     /**
708      * Returns a map of distinct taxonomies of
709      * all external nodes of node.
710      * If at least one of the external nodes has no taxonomy,
711      * null is returned.
712      * 
713      */
714     public static SortedMap<Taxonomy, Integer> obtainDistinctTaxonomyCounts( final PhylogenyNode node ) {
715         final List<PhylogenyNode> descs = node.getAllExternalDescendants();
716         final SortedMap<Taxonomy, Integer> tax_map = new TreeMap<Taxonomy, Integer>();
717         for( final PhylogenyNode n : descs ) {
718             if ( !n.getNodeData().isHasTaxonomy() || n.getNodeData().getTaxonomy().isEmpty() ) {
719                 return null;
720             }
721             final Taxonomy t = n.getNodeData().getTaxonomy();
722             if ( tax_map.containsKey( t ) ) {
723                 tax_map.put( t, tax_map.get( t ) + 1 );
724             }
725             else {
726                 tax_map.put( t, 1 );
727             }
728         }
729         return tax_map;
730     }
731
732     /**
733      * Arranges the order of childern for each node of this Phylogeny in such a
734      * way that either the branch with more children is on top (right) or on
735      * bottom (left), dependent on the value of boolean order.
736      * 
737      * @param order
738      *            decides in which direction to order
739      * @param pri 
740      */
741     public static void orderAppearance( final PhylogenyNode n,
742                                         final boolean order,
743                                         final boolean order_ext_alphabetically,
744                                         final DESCENDANT_SORT_PRIORITY pri ) {
745         if ( n.isExternal() ) {
746             return;
747         }
748         else {
749             PhylogenyNode temp = null;
750             if ( ( n.getNumberOfDescendants() == 2 )
751                     && ( n.getChildNode1().getNumberOfExternalNodes() != n.getChildNode2().getNumberOfExternalNodes() )
752                     && ( ( n.getChildNode1().getNumberOfExternalNodes() < n.getChildNode2().getNumberOfExternalNodes() ) == order ) ) {
753                 temp = n.getChildNode1();
754                 n.setChild1( n.getChildNode2() );
755                 n.setChild2( temp );
756             }
757             else if ( order_ext_alphabetically ) {
758                 boolean all_ext = true;
759                 for( final PhylogenyNode i : n.getDescendants() ) {
760                     if ( !i.isExternal() ) {
761                         all_ext = false;
762                         break;
763                     }
764                 }
765                 if ( all_ext ) {
766                     PhylogenyMethods.sortNodeDescendents( n, pri );
767                 }
768             }
769             for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
770                 orderAppearance( n.getChildNode( i ), order, order_ext_alphabetically, pri );
771             }
772         }
773     }
774
775     public static void postorderBranchColorAveragingExternalNodeBased( final Phylogeny p ) {
776         for( final PhylogenyNodeIterator iter = p.iteratorPostorder(); iter.hasNext(); ) {
777             final PhylogenyNode node = iter.next();
778             double red = 0.0;
779             double green = 0.0;
780             double blue = 0.0;
781             int n = 0;
782             if ( node.isInternal() ) {
783                 //for( final PhylogenyNodeIterator iterator = node.iterateChildNodesForward(); iterator.hasNext(); ) {
784                 for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
785                     final PhylogenyNode child_node = node.getChildNode( i );
786                     final Color child_color = getBranchColorValue( child_node );
787                     if ( child_color != null ) {
788                         ++n;
789                         red += child_color.getRed();
790                         green += child_color.getGreen();
791                         blue += child_color.getBlue();
792                     }
793                 }
794                 setBranchColorValue( node,
795                                      new Color( ForesterUtil.roundToInt( red / n ),
796                                                 ForesterUtil.roundToInt( green / n ),
797                                                 ForesterUtil.roundToInt( blue / n ) ) );
798             }
799         }
800     }
801
802     public static final void preOrderReId( final Phylogeny phy ) {
803         if ( phy.isEmpty() ) {
804             return;
805         }
806         phy.setIdToNodeMap( null );
807         long i = PhylogenyNode.getNodeCount();
808         for( final PhylogenyNodeIterator it = phy.iteratorPreorder(); it.hasNext(); ) {
809             it.next().setId( i++ );
810         }
811         PhylogenyNode.setNodeCount( i );
812     }
813
814     public final static Phylogeny[] readPhylogenies( final PhylogenyParser parser, final File file ) throws IOException {
815         final PhylogenyFactory factory = ParserBasedPhylogenyFactory.getInstance();
816         final Phylogeny[] trees = factory.create( file, parser );
817         if ( ( trees == null ) || ( trees.length == 0 ) ) {
818             throw new PhylogenyParserException( "Unable to parse phylogeny from file: " + file );
819         }
820         return trees;
821     }
822
823     public final static Phylogeny[] readPhylogenies( final PhylogenyParser parser, final List<File> files )
824             throws IOException {
825         final List<Phylogeny> tree_list = new ArrayList<Phylogeny>();
826         for( final File file : files ) {
827             final PhylogenyFactory factory = ParserBasedPhylogenyFactory.getInstance();
828             final Phylogeny[] trees = factory.create( file, parser );
829             if ( ( trees == null ) || ( trees.length == 0 ) ) {
830                 throw new PhylogenyParserException( "Unable to parse phylogeny from file: " + file );
831             }
832             tree_list.addAll( Arrays.asList( trees ) );
833         }
834         return tree_list.toArray( new Phylogeny[ tree_list.size() ] );
835     }
836
837     public static void removeNode( final PhylogenyNode remove_me, final Phylogeny phylogeny ) {
838         if ( remove_me.isRoot() ) {
839             if ( remove_me.getNumberOfDescendants() == 1 ) {
840                 final PhylogenyNode desc = remove_me.getDescendants().get( 0 );
841                 desc.setDistanceToParent( addPhylogenyDistances( remove_me.getDistanceToParent(),
842                                                                  desc.getDistanceToParent() ) );
843                 desc.setParent( null );
844                 phylogeny.setRoot( desc );
845                 phylogeny.clearHashIdToNodeMap();
846             }
847             else {
848                 throw new IllegalArgumentException( "attempt to remove a root node with more than one descendants" );
849             }
850         }
851         else if ( remove_me.isExternal() ) {
852             phylogeny.deleteSubtree( remove_me, false );
853             phylogeny.clearHashIdToNodeMap();
854             phylogeny.externalNodesHaveChanged();
855         }
856         else {
857             final PhylogenyNode parent = remove_me.getParent();
858             final List<PhylogenyNode> descs = remove_me.getDescendants();
859             parent.removeChildNode( remove_me );
860             for( final PhylogenyNode desc : descs ) {
861                 parent.addAsChild( desc );
862                 desc.setDistanceToParent( addPhylogenyDistances( remove_me.getDistanceToParent(),
863                                                                  desc.getDistanceToParent() ) );
864             }
865             remove_me.setParent( null );
866             phylogeny.clearHashIdToNodeMap();
867             phylogeny.externalNodesHaveChanged();
868         }
869     }
870
871     public static List<PhylogenyNode> searchData( final String query,
872                                                   final Phylogeny phy,
873                                                   final boolean case_sensitive,
874                                                   final boolean partial,
875                                                   final boolean search_domains ) {
876         final List<PhylogenyNode> nodes = new ArrayList<PhylogenyNode>();
877         if ( phy.isEmpty() || ( query == null ) ) {
878             return nodes;
879         }
880         if ( ForesterUtil.isEmpty( query ) ) {
881             return nodes;
882         }
883         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {
884             final PhylogenyNode node = iter.next();
885             boolean match = false;
886             if ( match( node.getName(), query, case_sensitive, partial ) ) {
887                 match = true;
888             }
889             else if ( node.getNodeData().isHasTaxonomy()
890                     && match( node.getNodeData().getTaxonomy().getTaxonomyCode(), query, case_sensitive, partial ) ) {
891                 match = true;
892             }
893             else if ( node.getNodeData().isHasTaxonomy()
894                     && match( node.getNodeData().getTaxonomy().getCommonName(), query, case_sensitive, partial ) ) {
895                 match = true;
896             }
897             else if ( node.getNodeData().isHasTaxonomy()
898                     && match( node.getNodeData().getTaxonomy().getScientificName(), query, case_sensitive, partial ) ) {
899                 match = true;
900             }
901             else if ( node.getNodeData().isHasTaxonomy()
902                     && ( node.getNodeData().getTaxonomy().getIdentifier() != null )
903                     && match( node.getNodeData().getTaxonomy().getIdentifier().getValue(),
904                               query,
905                               case_sensitive,
906                               partial ) ) {
907                 match = true;
908             }
909             else if ( node.getNodeData().isHasTaxonomy() && !node.getNodeData().getTaxonomy().getSynonyms().isEmpty() ) {
910                 final List<String> syns = node.getNodeData().getTaxonomy().getSynonyms();
911                 I: for( final String syn : syns ) {
912                     if ( match( syn, query, case_sensitive, partial ) ) {
913                         match = true;
914                         break I;
915                     }
916                 }
917             }
918             if ( !match && node.getNodeData().isHasSequence()
919                     && match( node.getNodeData().getSequence().getName(), query, case_sensitive, partial ) ) {
920                 match = true;
921             }
922             if ( !match && node.getNodeData().isHasSequence()
923                     && match( node.getNodeData().getSequence().getGeneName(), query, case_sensitive, partial ) ) {
924                 match = true;
925             }
926             if ( !match && node.getNodeData().isHasSequence()
927                     && match( node.getNodeData().getSequence().getSymbol(), query, case_sensitive, partial ) ) {
928                 match = true;
929             }
930             if ( !match
931                     && node.getNodeData().isHasSequence()
932                     && ( node.getNodeData().getSequence().getAccession() != null )
933                     && match( node.getNodeData().getSequence().getAccession().getValue(),
934                               query,
935                               case_sensitive,
936                               partial ) ) {
937                 match = true;
938             }
939             if ( search_domains && !match && node.getNodeData().isHasSequence()
940                     && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
941                 final DomainArchitecture da = node.getNodeData().getSequence().getDomainArchitecture();
942                 I: for( int i = 0; i < da.getNumberOfDomains(); ++i ) {
943                     if ( match( da.getDomain( i ).getName(), query, case_sensitive, partial ) ) {
944                         match = true;
945                         break I;
946                     }
947                 }
948             }
949             //
950             if ( !match && node.getNodeData().isHasSequence()
951                     && ( node.getNodeData().getSequence().getAnnotations() != null ) ) {
952                 for( final Annotation ann : node.getNodeData().getSequence().getAnnotations() ) {
953                     if ( match( ann.getDesc(), query, case_sensitive, partial ) ) {
954                         match = true;
955                         break;
956                     }
957                     if ( match( ann.getRef(), query, case_sensitive, partial ) ) {
958                         match = true;
959                         break;
960                     }
961                 }
962             }
963             if ( !match && node.getNodeData().isHasSequence()
964                     && ( node.getNodeData().getSequence().getCrossReferences() != null ) ) {
965                 for( final Accession x : node.getNodeData().getSequence().getCrossReferences() ) {
966                     if ( match( x.getComment(), query, case_sensitive, partial ) ) {
967                         match = true;
968                         break;
969                     }
970                     if ( match( x.getSource(), query, case_sensitive, partial ) ) {
971                         match = true;
972                         break;
973                     }
974                     if ( match( x.getValue(), query, case_sensitive, partial ) ) {
975                         match = true;
976                         break;
977                     }
978                 }
979             }
980             //
981             if ( !match && ( node.getNodeData().getBinaryCharacters() != null ) ) {
982                 Iterator<String> it = node.getNodeData().getBinaryCharacters().getPresentCharacters().iterator();
983                 I: while ( it.hasNext() ) {
984                     if ( match( it.next(), query, case_sensitive, partial ) ) {
985                         match = true;
986                         break I;
987                     }
988                 }
989                 it = node.getNodeData().getBinaryCharacters().getGainedCharacters().iterator();
990                 I: while ( it.hasNext() ) {
991                     if ( match( it.next(), query, case_sensitive, partial ) ) {
992                         match = true;
993                         break I;
994                     }
995                 }
996             }
997             if ( match ) {
998                 nodes.add( node );
999             }
1000         }
1001         return nodes;
1002     }
1003
1004     public static List<PhylogenyNode> searchDataLogicalAnd( final String[] queries,
1005                                                             final Phylogeny phy,
1006                                                             final boolean case_sensitive,
1007                                                             final boolean partial,
1008                                                             final boolean search_domains ) {
1009         final List<PhylogenyNode> nodes = new ArrayList<PhylogenyNode>();
1010         if ( phy.isEmpty() || ( queries == null ) || ( queries.length < 1 ) ) {
1011             return nodes;
1012         }
1013         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {
1014             final PhylogenyNode node = iter.next();
1015             boolean all_matched = true;
1016             for( final String query : queries ) {
1017                 boolean match = false;
1018                 if ( ForesterUtil.isEmpty( query ) ) {
1019                     continue;
1020                 }
1021                 if ( match( node.getName(), query, case_sensitive, partial ) ) {
1022                     match = true;
1023                 }
1024                 else if ( node.getNodeData().isHasTaxonomy()
1025                         && match( node.getNodeData().getTaxonomy().getTaxonomyCode(), query, case_sensitive, partial ) ) {
1026                     match = true;
1027                 }
1028                 else if ( node.getNodeData().isHasTaxonomy()
1029                         && match( node.getNodeData().getTaxonomy().getCommonName(), query, case_sensitive, partial ) ) {
1030                     match = true;
1031                 }
1032                 else if ( node.getNodeData().isHasTaxonomy()
1033                         && match( node.getNodeData().getTaxonomy().getScientificName(), query, case_sensitive, partial ) ) {
1034                     match = true;
1035                 }
1036                 else if ( node.getNodeData().isHasTaxonomy()
1037                         && ( node.getNodeData().getTaxonomy().getIdentifier() != null )
1038                         && match( node.getNodeData().getTaxonomy().getIdentifier().getValue(),
1039                                   query,
1040                                   case_sensitive,
1041                                   partial ) ) {
1042                     match = true;
1043                 }
1044                 else if ( node.getNodeData().isHasTaxonomy()
1045                         && !node.getNodeData().getTaxonomy().getSynonyms().isEmpty() ) {
1046                     final List<String> syns = node.getNodeData().getTaxonomy().getSynonyms();
1047                     I: for( final String syn : syns ) {
1048                         if ( match( syn, query, case_sensitive, partial ) ) {
1049                             match = true;
1050                             break I;
1051                         }
1052                     }
1053                 }
1054                 if ( !match && node.getNodeData().isHasSequence()
1055                         && match( node.getNodeData().getSequence().getName(), query, case_sensitive, partial ) ) {
1056                     match = true;
1057                 }
1058                 if ( !match && node.getNodeData().isHasSequence()
1059                         && match( node.getNodeData().getSequence().getGeneName(), query, case_sensitive, partial ) ) {
1060                     match = true;
1061                 }
1062                 if ( !match && node.getNodeData().isHasSequence()
1063                         && match( node.getNodeData().getSequence().getSymbol(), query, case_sensitive, partial ) ) {
1064                     match = true;
1065                 }
1066                 if ( !match
1067                         && node.getNodeData().isHasSequence()
1068                         && ( node.getNodeData().getSequence().getAccession() != null )
1069                         && match( node.getNodeData().getSequence().getAccession().getValue(),
1070                                   query,
1071                                   case_sensitive,
1072                                   partial ) ) {
1073                     match = true;
1074                 }
1075                 if ( search_domains && !match && node.getNodeData().isHasSequence()
1076                         && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
1077                     final DomainArchitecture da = node.getNodeData().getSequence().getDomainArchitecture();
1078                     I: for( int i = 0; i < da.getNumberOfDomains(); ++i ) {
1079                         if ( match( da.getDomain( i ).getName(), query, case_sensitive, partial ) ) {
1080                             match = true;
1081                             break I;
1082                         }
1083                     }
1084                 }
1085                 //
1086                 if ( !match && node.getNodeData().isHasSequence()
1087                         && ( node.getNodeData().getSequence().getAnnotations() != null ) ) {
1088                     for( final Annotation ann : node.getNodeData().getSequence().getAnnotations() ) {
1089                         if ( match( ann.getDesc(), query, case_sensitive, partial ) ) {
1090                             match = true;
1091                             break;
1092                         }
1093                         if ( match( ann.getRef(), query, case_sensitive, partial ) ) {
1094                             match = true;
1095                             break;
1096                         }
1097                     }
1098                 }
1099                 if ( !match && node.getNodeData().isHasSequence()
1100                         && ( node.getNodeData().getSequence().getCrossReferences() != null ) ) {
1101                     for( final Accession x : node.getNodeData().getSequence().getCrossReferences() ) {
1102                         if ( match( x.getComment(), query, case_sensitive, partial ) ) {
1103                             match = true;
1104                             break;
1105                         }
1106                         if ( match( x.getSource(), query, case_sensitive, partial ) ) {
1107                             match = true;
1108                             break;
1109                         }
1110                         if ( match( x.getValue(), query, case_sensitive, partial ) ) {
1111                             match = true;
1112                             break;
1113                         }
1114                     }
1115                 }
1116                 //
1117                 if ( !match && ( node.getNodeData().getBinaryCharacters() != null ) ) {
1118                     Iterator<String> it = node.getNodeData().getBinaryCharacters().getPresentCharacters().iterator();
1119                     I: while ( it.hasNext() ) {
1120                         if ( match( it.next(), query, case_sensitive, partial ) ) {
1121                             match = true;
1122                             break I;
1123                         }
1124                     }
1125                     it = node.getNodeData().getBinaryCharacters().getGainedCharacters().iterator();
1126                     I: while ( it.hasNext() ) {
1127                         if ( match( it.next(), query, case_sensitive, partial ) ) {
1128                             match = true;
1129                             break I;
1130                         }
1131                     }
1132                 }
1133                 if ( !match ) {
1134                     all_matched = false;
1135                     break;
1136                 }
1137             }
1138             if ( all_matched ) {
1139                 nodes.add( node );
1140             }
1141         }
1142         return nodes;
1143     }
1144
1145     /**
1146      * Convenience method.
1147      * Sets value for the first confidence value (created if not present, values overwritten otherwise). 
1148      */
1149     public static void setBootstrapConfidence( final PhylogenyNode node, final double bootstrap_confidence_value ) {
1150         setConfidence( node, bootstrap_confidence_value, "bootstrap" );
1151     }
1152
1153     public static void setBranchColorValue( final PhylogenyNode node, final Color color ) {
1154         if ( node.getBranchData().getBranchColor() == null ) {
1155             node.getBranchData().setBranchColor( new BranchColor() );
1156         }
1157         node.getBranchData().getBranchColor().setValue( color );
1158     }
1159
1160     /**
1161      * Convenience method
1162      */
1163     public static void setBranchWidthValue( final PhylogenyNode node, final double branch_width_value ) {
1164         node.getBranchData().setBranchWidth( new BranchWidth( branch_width_value ) );
1165     }
1166
1167     /**
1168      * Convenience method.
1169      * Sets value for the first confidence value (created if not present, values overwritten otherwise). 
1170      */
1171     public static void setConfidence( final PhylogenyNode node, final double confidence_value ) {
1172         setConfidence( node, confidence_value, "" );
1173     }
1174
1175     /**
1176      * Convenience method.
1177      * Sets value for the first confidence value (created if not present, values overwritten otherwise). 
1178      */
1179     public static void setConfidence( final PhylogenyNode node, final double confidence_value, final String type ) {
1180         Confidence c = null;
1181         if ( node.getBranchData().getNumberOfConfidences() > 0 ) {
1182             c = node.getBranchData().getConfidence( 0 );
1183         }
1184         else {
1185             c = new Confidence();
1186             node.getBranchData().addConfidence( c );
1187         }
1188         c.setType( type );
1189         c.setValue( confidence_value );
1190     }
1191
1192     public static void setScientificName( final PhylogenyNode node, final String scientific_name ) {
1193         if ( !node.getNodeData().isHasTaxonomy() ) {
1194             node.getNodeData().setTaxonomy( new Taxonomy() );
1195         }
1196         node.getNodeData().getTaxonomy().setScientificName( scientific_name );
1197     }
1198
1199     /**
1200      * Convenience method to set the taxonomy code of a phylogeny node.
1201      * 
1202      * 
1203      * @param node
1204      * @param taxonomy_code
1205      * @throws PhyloXmlDataFormatException 
1206      */
1207     public static void setTaxonomyCode( final PhylogenyNode node, final String taxonomy_code )
1208             throws PhyloXmlDataFormatException {
1209         if ( !node.getNodeData().isHasTaxonomy() ) {
1210             node.getNodeData().setTaxonomy( new Taxonomy() );
1211         }
1212         node.getNodeData().getTaxonomy().setTaxonomyCode( taxonomy_code );
1213     }
1214
1215     final static public void sortNodeDescendents( final PhylogenyNode node, final DESCENDANT_SORT_PRIORITY pri ) {
1216         class PhylogenyNodeSortTaxonomyPriority implements Comparator<PhylogenyNode> {
1217
1218             @Override
1219             public int compare( final PhylogenyNode n1, final PhylogenyNode n2 ) {
1220                 if ( n1.getNodeData().isHasTaxonomy() && n2.getNodeData().isHasTaxonomy() ) {
1221                     if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getTaxonomy().getScientificName() ) )
1222                             && ( !ForesterUtil.isEmpty( n2.getNodeData().getTaxonomy().getScientificName() ) ) ) {
1223                         return n1.getNodeData().getTaxonomy().getScientificName().toLowerCase()
1224                                 .compareTo( n2.getNodeData().getTaxonomy().getScientificName().toLowerCase() );
1225                     }
1226                     if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getTaxonomy().getTaxonomyCode() ) )
1227                             && ( !ForesterUtil.isEmpty( n2.getNodeData().getTaxonomy().getTaxonomyCode() ) ) ) {
1228                         return n1.getNodeData().getTaxonomy().getTaxonomyCode()
1229                                 .compareTo( n2.getNodeData().getTaxonomy().getTaxonomyCode() );
1230                     }
1231                     if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getTaxonomy().getCommonName() ) )
1232                             && ( !ForesterUtil.isEmpty( n2.getNodeData().getTaxonomy().getCommonName() ) ) ) {
1233                         return n1.getNodeData().getTaxonomy().getCommonName().toLowerCase()
1234                                 .compareTo( n2.getNodeData().getTaxonomy().getCommonName().toLowerCase() );
1235                     }
1236                 }
1237                 if ( n1.getNodeData().isHasSequence() && n2.getNodeData().isHasSequence() ) {
1238                     if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getName() ) )
1239                             && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getName() ) ) ) {
1240                         return n1.getNodeData().getSequence().getName().toLowerCase()
1241                                 .compareTo( n2.getNodeData().getSequence().getName().toLowerCase() );
1242                     }
1243                     if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getSymbol() ) )
1244                             && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getSymbol() ) ) ) {
1245                         return n1.getNodeData().getSequence().getSymbol()
1246                                 .compareTo( n2.getNodeData().getSequence().getSymbol() );
1247                     }
1248                     if ( ( n1.getNodeData().getSequence().getAccession() != null )
1249                             && ( n2.getNodeData().getSequence().getAccession() != null )
1250                             && !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getAccession().getValue() )
1251                             && !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getAccession().getValue() ) ) {
1252                         return n1.getNodeData().getSequence().getAccession().getValue()
1253                                 .compareTo( n2.getNodeData().getSequence().getAccession().getValue() );
1254                     }
1255                 }
1256                 if ( ( !ForesterUtil.isEmpty( n1.getName() ) ) && ( !ForesterUtil.isEmpty( n2.getName() ) ) ) {
1257                     return n1.getName().toLowerCase().compareTo( n2.getName().toLowerCase() );
1258                 }
1259                 return 0;
1260             }
1261         }
1262         class PhylogenyNodeSortSequencePriority implements Comparator<PhylogenyNode> {
1263
1264             @Override
1265             public int compare( final PhylogenyNode n1, final PhylogenyNode n2 ) {
1266                 if ( n1.getNodeData().isHasSequence() && n2.getNodeData().isHasSequence() ) {
1267                     if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getName() ) )
1268                             && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getName() ) ) ) {
1269                         return n1.getNodeData().getSequence().getName().toLowerCase()
1270                                 .compareTo( n2.getNodeData().getSequence().getName().toLowerCase() );
1271                     }
1272                     if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getSymbol() ) )
1273                             && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getSymbol() ) ) ) {
1274                         return n1.getNodeData().getSequence().getSymbol()
1275                                 .compareTo( n2.getNodeData().getSequence().getSymbol() );
1276                     }
1277                     if ( ( n1.getNodeData().getSequence().getAccession() != null )
1278                             && ( n2.getNodeData().getSequence().getAccession() != null )
1279                             && !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getAccession().getValue() )
1280                             && !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getAccession().getValue() ) ) {
1281                         return n1.getNodeData().getSequence().getAccession().getValue()
1282                                 .compareTo( n2.getNodeData().getSequence().getAccession().getValue() );
1283                     }
1284                 }
1285                 if ( n1.getNodeData().isHasTaxonomy() && n2.getNodeData().isHasTaxonomy() ) {
1286                     if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getTaxonomy().getScientificName() ) )
1287                             && ( !ForesterUtil.isEmpty( n2.getNodeData().getTaxonomy().getScientificName() ) ) ) {
1288                         return n1.getNodeData().getTaxonomy().getScientificName().toLowerCase()
1289                                 .compareTo( n2.getNodeData().getTaxonomy().getScientificName().toLowerCase() );
1290                     }
1291                     if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getTaxonomy().getTaxonomyCode() ) )
1292                             && ( !ForesterUtil.isEmpty( n2.getNodeData().getTaxonomy().getTaxonomyCode() ) ) ) {
1293                         return n1.getNodeData().getTaxonomy().getTaxonomyCode()
1294                                 .compareTo( n2.getNodeData().getTaxonomy().getTaxonomyCode() );
1295                     }
1296                     if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getTaxonomy().getCommonName() ) )
1297                             && ( !ForesterUtil.isEmpty( n2.getNodeData().getTaxonomy().getCommonName() ) ) ) {
1298                         return n1.getNodeData().getTaxonomy().getCommonName().toLowerCase()
1299                                 .compareTo( n2.getNodeData().getTaxonomy().getCommonName().toLowerCase() );
1300                     }
1301                 }
1302                 if ( ( !ForesterUtil.isEmpty( n1.getName() ) ) && ( !ForesterUtil.isEmpty( n2.getName() ) ) ) {
1303                     return n1.getName().toLowerCase().compareTo( n2.getName().toLowerCase() );
1304                 }
1305                 return 0;
1306             }
1307         }
1308         class PhylogenyNodeSortNodeNamePriority implements Comparator<PhylogenyNode> {
1309
1310             @Override
1311             public int compare( final PhylogenyNode n1, final PhylogenyNode n2 ) {
1312                 if ( ( !ForesterUtil.isEmpty( n1.getName() ) ) && ( !ForesterUtil.isEmpty( n2.getName() ) ) ) {
1313                     return n1.getName().toLowerCase().compareTo( n2.getName().toLowerCase() );
1314                 }
1315                 if ( n1.getNodeData().isHasTaxonomy() && n2.getNodeData().isHasTaxonomy() ) {
1316                     if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getTaxonomy().getScientificName() ) )
1317                             && ( !ForesterUtil.isEmpty( n2.getNodeData().getTaxonomy().getScientificName() ) ) ) {
1318                         return n1.getNodeData().getTaxonomy().getScientificName().toLowerCase()
1319                                 .compareTo( n2.getNodeData().getTaxonomy().getScientificName().toLowerCase() );
1320                     }
1321                     if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getTaxonomy().getTaxonomyCode() ) )
1322                             && ( !ForesterUtil.isEmpty( n2.getNodeData().getTaxonomy().getTaxonomyCode() ) ) ) {
1323                         return n1.getNodeData().getTaxonomy().getTaxonomyCode()
1324                                 .compareTo( n2.getNodeData().getTaxonomy().getTaxonomyCode() );
1325                     }
1326                     if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getTaxonomy().getCommonName() ) )
1327                             && ( !ForesterUtil.isEmpty( n2.getNodeData().getTaxonomy().getCommonName() ) ) ) {
1328                         return n1.getNodeData().getTaxonomy().getCommonName().toLowerCase()
1329                                 .compareTo( n2.getNodeData().getTaxonomy().getCommonName().toLowerCase() );
1330                     }
1331                 }
1332                 if ( n1.getNodeData().isHasSequence() && n2.getNodeData().isHasSequence() ) {
1333                     if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getName() ) )
1334                             && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getName() ) ) ) {
1335                         return n1.getNodeData().getSequence().getName().toLowerCase()
1336                                 .compareTo( n2.getNodeData().getSequence().getName().toLowerCase() );
1337                     }
1338                     if ( ( !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getSymbol() ) )
1339                             && ( !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getSymbol() ) ) ) {
1340                         return n1.getNodeData().getSequence().getSymbol()
1341                                 .compareTo( n2.getNodeData().getSequence().getSymbol() );
1342                     }
1343                     if ( ( n1.getNodeData().getSequence().getAccession() != null )
1344                             && ( n2.getNodeData().getSequence().getAccession() != null )
1345                             && !ForesterUtil.isEmpty( n1.getNodeData().getSequence().getAccession().getValue() )
1346                             && !ForesterUtil.isEmpty( n2.getNodeData().getSequence().getAccession().getValue() ) ) {
1347                         return n1.getNodeData().getSequence().getAccession().getValue()
1348                                 .compareTo( n2.getNodeData().getSequence().getAccession().getValue() );
1349                     }
1350                 }
1351                 return 0;
1352             }
1353         }
1354         Comparator<PhylogenyNode> c;
1355         switch ( pri ) {
1356             case SEQUENCE:
1357                 c = new PhylogenyNodeSortSequencePriority();
1358                 break;
1359             case NODE_NAME:
1360                 c = new PhylogenyNodeSortNodeNamePriority();
1361                 break;
1362             default:
1363                 c = new PhylogenyNodeSortTaxonomyPriority();
1364         }
1365         final List<PhylogenyNode> descs = node.getDescendants();
1366         Collections.sort( descs, c );
1367         int i = 0;
1368         for( final PhylogenyNode desc : descs ) {
1369             node.setChildNode( i++, desc );
1370         }
1371     }
1372
1373     /**
1374      * Removes from Phylogeny to_be_stripped all external Nodes which are
1375      * associated with a species NOT found in Phylogeny reference.
1376      * 
1377      * @param reference
1378      *            a reference Phylogeny
1379      * @param to_be_stripped
1380      *            Phylogeny to be stripped
1381      * @return nodes removed from to_be_stripped
1382      */
1383     public static List<PhylogenyNode> taxonomyBasedDeletionOfExternalNodes( final Phylogeny reference,
1384                                                                             final Phylogeny to_be_stripped ) {
1385         final Set<String> ref_ext_taxo = new HashSet<String>();
1386         for( final PhylogenyNodeIterator it = reference.iteratorExternalForward(); it.hasNext(); ) {
1387             final PhylogenyNode n = it.next();
1388             if ( !n.getNodeData().isHasTaxonomy() ) {
1389                 throw new IllegalArgumentException( "no taxonomic data in node: " + n );
1390             }
1391             if ( !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getScientificName() ) ) {
1392                 ref_ext_taxo.add( n.getNodeData().getTaxonomy().getScientificName() );
1393             }
1394             if ( !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
1395                 ref_ext_taxo.add( n.getNodeData().getTaxonomy().getTaxonomyCode() );
1396             }
1397             if ( ( n.getNodeData().getTaxonomy().getIdentifier() != null )
1398                     && !ForesterUtil.isEmpty( n.getNodeData().getTaxonomy().getIdentifier().getValue() ) ) {
1399                 ref_ext_taxo.add( n.getNodeData().getTaxonomy().getIdentifier().getValuePlusProvider() );
1400             }
1401         }
1402         final ArrayList<PhylogenyNode> nodes_to_delete = new ArrayList<PhylogenyNode>();
1403         for( final PhylogenyNodeIterator it = to_be_stripped.iteratorExternalForward(); it.hasNext(); ) {
1404             final PhylogenyNode n = it.next();
1405             if ( !n.getNodeData().isHasTaxonomy() ) {
1406                 nodes_to_delete.add( n );
1407             }
1408             else if ( !( ref_ext_taxo.contains( n.getNodeData().getTaxonomy().getScientificName() ) )
1409                     && !( ref_ext_taxo.contains( n.getNodeData().getTaxonomy().getTaxonomyCode() ) )
1410                     && !( ( n.getNodeData().getTaxonomy().getIdentifier() != null ) && ref_ext_taxo.contains( n
1411                             .getNodeData().getTaxonomy().getIdentifier().getValuePlusProvider() ) ) ) {
1412                 nodes_to_delete.add( n );
1413             }
1414         }
1415         for( final PhylogenyNode n : nodes_to_delete ) {
1416             to_be_stripped.deleteSubtree( n, true );
1417         }
1418         to_be_stripped.clearHashIdToNodeMap();
1419         to_be_stripped.externalNodesHaveChanged();
1420         return nodes_to_delete;
1421     }
1422
1423     final static public void transferInternalNamesToBootstrapSupport( final Phylogeny phy ) {
1424         final PhylogenyNodeIterator it = phy.iteratorPostorder();
1425         while ( it.hasNext() ) {
1426             final PhylogenyNode n = it.next();
1427             if ( !n.isExternal() && !ForesterUtil.isEmpty( n.getName() ) ) {
1428                 double value = -1;
1429                 try {
1430                     value = Double.parseDouble( n.getName() );
1431                 }
1432                 catch ( final NumberFormatException e ) {
1433                     throw new IllegalArgumentException( "failed to parse number from [" + n.getName() + "]: "
1434                             + e.getLocalizedMessage() );
1435                 }
1436                 if ( value >= 0.0 ) {
1437                     n.getBranchData().addConfidence( new Confidence( value, "bootstrap" ) );
1438                     n.setName( "" );
1439                 }
1440             }
1441         }
1442     }
1443
1444     final static public void transferInternalNodeNamesToConfidence( final Phylogeny phy ) {
1445         final PhylogenyNodeIterator it = phy.iteratorPostorder();
1446         while ( it.hasNext() ) {
1447             final PhylogenyNode n = it.next();
1448             if ( !n.isExternal() && !n.getBranchData().isHasConfidences() ) {
1449                 if ( !ForesterUtil.isEmpty( n.getName() ) ) {
1450                     double d = -1.0;
1451                     try {
1452                         d = Double.parseDouble( n.getName() );
1453                     }
1454                     catch ( final Exception e ) {
1455                         d = -1.0;
1456                     }
1457                     if ( d >= 0.0 ) {
1458                         n.getBranchData().addConfidence( new Confidence( d, "" ) );
1459                         n.setName( "" );
1460                     }
1461                 }
1462             }
1463         }
1464     }
1465
1466     final static public void transferNodeNameToField( final Phylogeny phy,
1467                                                       final PhylogenyNodeField field,
1468                                                       final boolean external_only ) throws PhyloXmlDataFormatException {
1469         final PhylogenyNodeIterator it = phy.iteratorPostorder();
1470         while ( it.hasNext() ) {
1471             final PhylogenyNode n = it.next();
1472             if ( external_only && n.isInternal() ) {
1473                 continue;
1474             }
1475             final String name = n.getName().trim();
1476             if ( !ForesterUtil.isEmpty( name ) ) {
1477                 switch ( field ) {
1478                     case TAXONOMY_CODE:
1479                         n.setName( "" );
1480                         setTaxonomyCode( n, name );
1481                         break;
1482                     case TAXONOMY_SCIENTIFIC_NAME:
1483                         n.setName( "" );
1484                         if ( !n.getNodeData().isHasTaxonomy() ) {
1485                             n.getNodeData().setTaxonomy( new Taxonomy() );
1486                         }
1487                         n.getNodeData().getTaxonomy().setScientificName( name );
1488                         break;
1489                     case TAXONOMY_COMMON_NAME:
1490                         n.setName( "" );
1491                         if ( !n.getNodeData().isHasTaxonomy() ) {
1492                             n.getNodeData().setTaxonomy( new Taxonomy() );
1493                         }
1494                         n.getNodeData().getTaxonomy().setCommonName( name );
1495                         break;
1496                     case SEQUENCE_SYMBOL:
1497                         n.setName( "" );
1498                         if ( !n.getNodeData().isHasSequence() ) {
1499                             n.getNodeData().setSequence( new Sequence() );
1500                         }
1501                         n.getNodeData().getSequence().setSymbol( name );
1502                         break;
1503                     case SEQUENCE_NAME:
1504                         n.setName( "" );
1505                         if ( !n.getNodeData().isHasSequence() ) {
1506                             n.getNodeData().setSequence( new Sequence() );
1507                         }
1508                         n.getNodeData().getSequence().setName( name );
1509                         break;
1510                     case TAXONOMY_ID_UNIPROT_1: {
1511                         if ( !n.getNodeData().isHasTaxonomy() ) {
1512                             n.getNodeData().setTaxonomy( new Taxonomy() );
1513                         }
1514                         String id = name;
1515                         final int i = name.indexOf( '_' );
1516                         if ( i > 0 ) {
1517                             id = name.substring( 0, i );
1518                         }
1519                         else {
1520                             n.setName( "" );
1521                         }
1522                         n.getNodeData().getTaxonomy()
1523                                 .setIdentifier( new Identifier( id, PhyloXmlUtil.UNIPROT_TAX_PROVIDER ) );
1524                         break;
1525                     }
1526                     case TAXONOMY_ID_UNIPROT_2: {
1527                         if ( !n.getNodeData().isHasTaxonomy() ) {
1528                             n.getNodeData().setTaxonomy( new Taxonomy() );
1529                         }
1530                         String id = name;
1531                         final int i = name.indexOf( '_' );
1532                         if ( i > 0 ) {
1533                             id = name.substring( i + 1, name.length() );
1534                         }
1535                         else {
1536                             n.setName( "" );
1537                         }
1538                         n.getNodeData().getTaxonomy()
1539                                 .setIdentifier( new Identifier( id, PhyloXmlUtil.UNIPROT_TAX_PROVIDER ) );
1540                         break;
1541                     }
1542                     case TAXONOMY_ID: {
1543                         if ( !n.getNodeData().isHasTaxonomy() ) {
1544                             n.getNodeData().setTaxonomy( new Taxonomy() );
1545                         }
1546                         n.getNodeData().getTaxonomy().setIdentifier( new Identifier( name ) );
1547                         break;
1548                     }
1549                 }
1550             }
1551         }
1552     }
1553
1554     static double addPhylogenyDistances( final double a, final double b ) {
1555         if ( ( a >= 0.0 ) && ( b >= 0.0 ) ) {
1556             return a + b;
1557         }
1558         else if ( a >= 0.0 ) {
1559             return a;
1560         }
1561         else if ( b >= 0.0 ) {
1562             return b;
1563         }
1564         return PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT;
1565     }
1566
1567     static double calculateDistanceToAncestor( final PhylogenyNode anc, PhylogenyNode desc ) {
1568         double d = 0;
1569         boolean all_default = true;
1570         while ( anc != desc ) {
1571             if ( desc.getDistanceToParent() != PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ) {
1572                 d += desc.getDistanceToParent();
1573                 if ( all_default ) {
1574                     all_default = false;
1575                 }
1576             }
1577             desc = desc.getParent();
1578         }
1579         if ( all_default ) {
1580             return PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT;
1581         }
1582         return d;
1583     }
1584
1585     /**
1586      * Deep copies the phylogeny originating from this node.
1587      */
1588     static PhylogenyNode copySubTree( final PhylogenyNode source ) {
1589         if ( source == null ) {
1590             return null;
1591         }
1592         else {
1593             final PhylogenyNode newnode = source.copyNodeData();
1594             if ( !source.isExternal() ) {
1595                 for( int i = 0; i < source.getNumberOfDescendants(); ++i ) {
1596                     newnode.setChildNode( i, PhylogenyMethods.copySubTree( source.getChildNode( i ) ) );
1597                 }
1598             }
1599             return newnode;
1600         }
1601     }
1602
1603     /**
1604      * Shallow copies the phylogeny originating from this node.
1605      */
1606     static PhylogenyNode copySubTreeShallow( final PhylogenyNode source ) {
1607         if ( source == null ) {
1608             return null;
1609         }
1610         else {
1611             final PhylogenyNode newnode = source.copyNodeDataShallow();
1612             if ( !source.isExternal() ) {
1613                 for( int i = 0; i < source.getNumberOfDescendants(); ++i ) {
1614                     newnode.setChildNode( i, PhylogenyMethods.copySubTreeShallow( source.getChildNode( i ) ) );
1615                 }
1616             }
1617             return newnode;
1618         }
1619     }
1620
1621     /**
1622      * Calculates the distance between PhylogenyNodes n1 and n2.
1623      * PRECONDITION: n1 is a descendant of n2.
1624      * 
1625      * @param n1
1626      *            a descendant of n2
1627      * @param n2
1628      * @return distance between n1 and n2
1629      */
1630     private static double getDistance( PhylogenyNode n1, final PhylogenyNode n2 ) {
1631         double d = 0.0;
1632         while ( n1 != n2 ) {
1633             if ( n1.getDistanceToParent() > 0.0 ) {
1634                 d += n1.getDistanceToParent();
1635             }
1636             n1 = n1.getParent();
1637         }
1638         return d;
1639     }
1640
1641     private static boolean match( final String s,
1642                                   final String query,
1643                                   final boolean case_sensitive,
1644                                   final boolean partial ) {
1645         if ( ForesterUtil.isEmpty( s ) || ForesterUtil.isEmpty( query ) ) {
1646             return false;
1647         }
1648         String my_s = s.trim();
1649         String my_query = query.trim();
1650         if ( !case_sensitive ) {
1651             my_s = my_s.toLowerCase();
1652             my_query = my_query.toLowerCase();
1653         }
1654         if ( partial ) {
1655             return my_s.indexOf( my_query ) >= 0;
1656         }
1657         else {
1658             return my_s.equals( my_query );
1659         }
1660     }
1661
1662     public static enum DESCENDANT_SORT_PRIORITY {
1663         TAXONOMY, SEQUENCE, NODE_NAME;
1664     }
1665
1666     public static enum PhylogenyNodeField {
1667         CLADE_NAME,
1668         TAXONOMY_CODE,
1669         TAXONOMY_SCIENTIFIC_NAME,
1670         TAXONOMY_COMMON_NAME,
1671         SEQUENCE_SYMBOL,
1672         SEQUENCE_NAME,
1673         TAXONOMY_ID_UNIPROT_1,
1674         TAXONOMY_ID_UNIPROT_2,
1675         TAXONOMY_ID;
1676     }
1677 }