09901711297537b04db31c3f7f8d3b9e7176cd80
[jalview.git] / forester / java / src / org / forester / phylogeny / Phylogeny.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 // Copyright (C) 2000-2001 Washington University School of Medicine
8 // and Howard Hughes Medical Institute
9 // All rights reserved
10 //
11 // This library is free software; you can redistribute it and/or
12 // modify it under the terms of the GNU Lesser General Public
13 // License as published by the Free Software Foundation; either
14 // version 2.1 of the License, or (at your option) any later version.
15 //
16 // This library is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 // Lesser General Public License for more details.
20 //
21 // You should have received a copy of the GNU Lesser General Public
22 // License along with this library; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 //
25 // Contact: phylosoft @ gmail . com
26 // WWW: https://sites.google.com/site/cmzmasek/home/software/forester
27
28 package org.forester.phylogeny;
29
30 import java.io.IOException;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collection;
34 import java.util.HashMap;
35 import java.util.Iterator;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.NoSuchElementException;
39 import java.util.Vector;
40
41 import org.forester.io.parsers.nhx.NHXParser;
42 import org.forester.io.writers.PhylogenyWriter;
43 import org.forester.phylogeny.PhylogenyNode.NH_CONVERSION_SUPPORT_VALUE_STYLE;
44 import org.forester.phylogeny.data.BranchData;
45 import org.forester.phylogeny.data.Confidence;
46 import org.forester.phylogeny.data.Identifier;
47 import org.forester.phylogeny.data.PhylogenyDataUtil;
48 import org.forester.phylogeny.data.Sequence;
49 import org.forester.phylogeny.data.SequenceRelation;
50 import org.forester.phylogeny.data.SequenceRelation.SEQUENCE_RELATION_TYPE;
51 import org.forester.phylogeny.factories.ParserBasedPhylogenyFactory;
52 import org.forester.phylogeny.factories.PhylogenyFactory;
53 import org.forester.phylogeny.iterators.ExternalForwardIterator;
54 import org.forester.phylogeny.iterators.LevelOrderTreeIterator;
55 import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
56 import org.forester.phylogeny.iterators.PostorderTreeIterator;
57 import org.forester.phylogeny.iterators.PreorderTreeIterator;
58 import org.forester.util.FailedConditionCheckException;
59 import org.forester.util.ForesterUtil;
60
61 public class Phylogeny {
62
63     public final static boolean                                 ALLOW_MULTIPLE_PARENTS_DEFAULT = false;
64     private PhylogenyNode                                       _root;
65     private boolean                                             _rooted;
66     private boolean                                             _allow_multiple_parents;
67     private String                                              _name;
68     private String                                              _type;
69     private String                                              _description;
70     private String                                              _distance_unit;
71     private Confidence                                          _confidence;
72     private Identifier                                          _identifier;
73     private boolean                                             _rerootable;
74     private HashMap<Long, PhylogenyNode>                        _id_to_node_map;
75     private List<PhylogenyNode>                                 _external_nodes_set;
76     private Collection<Sequence>                                _sequenceRelationQueries;
77     private Collection<SequenceRelation.SEQUENCE_RELATION_TYPE> _relevant_sequence_relation_types;
78
79     /**
80      * Default Phylogeny constructor. Constructs an empty Phylogeny.
81      */
82     public Phylogeny() {
83         init();
84     }
85
86     /**
87      * Adds this Phylogeny to the list of child nodes of PhylogenyNode parent
88      * and sets the parent of this to parent.
89      *
90      * @param n
91      *            the PhylogenyNode to add
92      */
93     public void addAsChild( final PhylogenyNode parent ) {
94         if ( isEmpty() ) {
95             throw new IllegalArgumentException( "Attempt to add an empty tree." );
96         }
97         if ( !isRooted() ) {
98             throw new IllegalArgumentException( "Attempt to add an unrooted tree." );
99         }
100         parent.addAsChild( getRoot() );
101         externalNodesHaveChanged();
102     }
103
104     public void addAsSibling( final PhylogenyNode sibling ) {
105         if ( isEmpty() ) {
106             throw new IllegalArgumentException( "Attempt to add an empty tree." );
107         }
108         if ( !isRooted() ) {
109             throw new IllegalArgumentException( "Attempt to add an unrooted tree." );
110         }
111         final int sibling_index = sibling.getChildNodeIndex();
112         final PhylogenyNode new_node = new PhylogenyNode();
113         final PhylogenyNode sibling_parent = sibling.getParent();
114         new_node.setChild1( sibling );
115         new_node.setChild2( getRoot() );
116         new_node.setParent( sibling_parent );
117         sibling.setParent( new_node );
118         sibling_parent.setChildNode( sibling_index, new_node );
119         final double new_dist = sibling.getDistanceToParent() == PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ? PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT
120                 : sibling.getDistanceToParent() / 2;
121         new_node.setDistanceToParent( new_dist );
122         sibling.setDistanceToParent( new_dist );
123         externalNodesHaveChanged();
124     }
125
126     /**
127      * This calculates the height of the subtree emanating at n for rooted,
128      * tree-shaped phylogenies
129      *
130      * @param n
131      *            the root-node of a subtree
132      * @return the height of the subtree emanating at n
133      */
134     public double calculateSubtreeHeight( final PhylogenyNode n, final boolean take_collapse_into_account ) {
135         if ( n.isExternal() || ( take_collapse_into_account && n.isCollapse() ) ) {
136             return n.getDistanceToParent() > 0 ? n.getDistanceToParent() : 0;
137         }
138         else {
139             double max = -Double.MAX_VALUE;
140             for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
141                 final double l = calculateSubtreeHeight( n.getChildNode( i ),  take_collapse_into_account );
142                 if ( l > max ) {
143                     max = l;
144                 }
145             }
146             return max + ( n.getDistanceToParent() > 0 ? n.getDistanceToParent() : 0) ;
147         }
148     }
149
150     public void clearHashIdToNodeMap() {
151         setIdToNodeMap( null );
152     }
153
154     /**
155      * Returns a deep copy of this Phylogeny.
156      * <p>
157      * (The resulting Phylogeny has its references in the external nodes
158      * corrected, if they are lacking/obsolete in this.)
159      */
160     public Phylogeny copy() {
161         return copy( _root );
162     }
163
164     /**
165      * Returns a deep copy of this Phylogeny.
166      * <p>
167      * (The resulting Phylogeny has its references in the external nodes
168      * corrected, if they are lacking/obsolete in this.)
169      */
170     public Phylogeny copy( final PhylogenyNode source ) {
171         final Phylogeny tree = new Phylogeny();
172         if ( isEmpty() ) {
173             tree.init();
174             return tree;
175         }
176         tree._rooted = _rooted;
177         tree._name = new String( _name );
178         tree._description = new String( _description );
179         tree._type = new String( _type );
180         tree._rerootable = _rerootable;
181         tree._distance_unit = new String( _distance_unit );
182         if ( _confidence != null ) {
183             tree._confidence = ( Confidence ) _confidence.copy();
184         }
185         if ( _identifier != null ) {
186             tree._identifier = ( Identifier ) _identifier.copy();
187         }
188         tree.setAllowMultipleParents( isAllowMultipleParents() );
189         tree._root = PhylogenyMethods.copySubTree( source );
190         return tree;
191     }
192
193     /**
194      * Returns a shallow copy of this Phylogeny.
195      * <p>
196      * (The resulting Phylogeny has its references in the external nodes
197      * corrected, if they are lacking/obsolete in this.)
198      */
199     public Phylogeny copyShallow() {
200         return copyShallow( _root );
201     }
202
203     public Phylogeny copyShallow( final PhylogenyNode source ) {
204         final Phylogeny tree = new Phylogeny();
205         if ( isEmpty() ) {
206             tree.init();
207             return tree;
208         }
209         tree._rooted = _rooted;
210         tree._name = _name;
211         tree._description = _description;
212         tree._type = _type;
213         tree._rerootable = _rerootable;
214         tree._distance_unit = _distance_unit;
215         tree._confidence = _confidence;
216         tree._identifier = _identifier;
217         tree.setAllowMultipleParents( isAllowMultipleParents() );
218         tree._root = PhylogenyMethods.copySubTreeShallow( source );
219         return tree;
220     }
221
222     /**
223      * Need to call clearHashIdToNodeMap() afterwards (not done automatically
224      * to allow client multiple deletions in linear time).
225      * Need to call 'recalculateNumberOfExternalDescendants(boolean)' after this
226      * if tree is to be displayed.
227      *
228      * @param remove_us the parent node of the subtree to be deleted
229      */
230     public void deleteSubtree( final PhylogenyNode remove_us, final boolean collapse_resulting_node_with_one_desc ) {
231         if ( isEmpty() || ( remove_us.isRoot() && ( getNumberOfExternalNodes() != 1 ) ) ) {
232             return;
233         }
234         if ( remove_us.isRoot() && ( getNumberOfExternalNodes() == 1 ) ) {
235             init();
236         }
237         else if ( !collapse_resulting_node_with_one_desc ) {
238             remove_us.getParent().removeChildNode( remove_us );
239         }
240         else {
241             final PhylogenyNode removed_node = remove_us;
242             final PhylogenyNode p = remove_us.getParent();
243             if ( p.isRoot() ) {
244                 if ( p.getNumberOfDescendants() == 2 ) {
245                     if ( removed_node.isFirstChildNode() ) {
246                         setRoot( getRoot().getChildNode( 1 ) );
247                         getRoot().setParent( null );
248                     }
249                     else {
250                         setRoot( getRoot().getChildNode( 0 ) );
251                         getRoot().setParent( null );
252                     }
253                 }
254                 else {
255                     p.removeChildNode( removed_node.getChildNodeIndex() );
256                 }
257             }
258             else {
259                 final PhylogenyNode pp = removed_node.getParent().getParent();
260                 if ( p.getNumberOfDescendants() == 2 ) {
261                     final int pi = p.getChildNodeIndex();
262                     if ( removed_node.isFirstChildNode() ) {
263                         p.getChildNode( 1 ).setDistanceToParent( PhylogenyMethods.addPhylogenyDistances( p
264                                                                                                          .getDistanceToParent(), p.getChildNode( 1 ).getDistanceToParent() ) );
265                         pp.setChildNode( pi, p.getChildNode( 1 ) );
266                     }
267                     else {
268                         p.getChildNode( 0 ).setDistanceToParent( PhylogenyMethods.addPhylogenyDistances( p
269                                                                                                          .getDistanceToParent(), p.getChildNode( 0 ).getDistanceToParent() ) );
270                         pp.setChildNode( pi, p.getChildNode( 0 ) );
271                     }
272                 }
273                 else {
274                     p.removeChildNode( removed_node.getChildNodeIndex() );
275                 }
276             }
277         }
278         remove_us.removeConnections();
279         externalNodesHaveChanged();
280     }
281
282     public void externalNodesHaveChanged() {
283         _external_nodes_set = null;
284     }
285
286     public String[] getAllExternalNodeNames() {
287         int i = 0;
288         if ( isEmpty() ) {
289             return null;
290         }
291         final String[] names = new String[ getNumberOfExternalNodes() ];
292         for( final PhylogenyNodeIterator iter = iteratorExternalForward(); iter.hasNext(); ) {
293             names[ i++ ] = new String( iter.next().getName() );
294         }
295         return names;
296     }
297
298     public Confidence getConfidence() {
299         return _confidence;
300     }
301
302     public String getDescription() {
303         return _description;
304     }
305
306     public String getDistanceUnit() {
307         return _distance_unit;
308     }
309
310     public final static Phylogeny createInstanceFromNhxString( final String nhx ) throws IOException {
311         final PhylogenyFactory factory = ParserBasedPhylogenyFactory.getInstance();
312         return factory.create( nhx, new NHXParser() )[ 0 ];
313     }
314
315     /**
316      *
317      * Warning. The order of the returned nodes is random
318      * -- and hence cannot be relied on.
319      *
320      * @return Unordered set of PhylogenyNode
321      */
322     public List<PhylogenyNode> getExternalNodes() {
323         if ( _external_nodes_set == null ) {
324             _external_nodes_set = new ArrayList<PhylogenyNode>();
325             for( final PhylogenyNodeIterator it = iteratorPostorder(); it.hasNext(); ) {
326                 final PhylogenyNode n = it.next();
327                 if ( n.isExternal() ) {
328                     _external_nodes_set.add( n );
329                 }
330             }
331         }
332         return _external_nodes_set;
333     }
334
335     /**
336      * Returns the number of duplications of this Phylogeny (int). A return
337      * value of -1 indicates that the number of duplications is unknown.
338      */
339     // public int getNumberOfDuplications() {
340     // return _number_of_duplications;
341     // } // getNumberOfDuplications()
342     /**
343      * Sets the number of duplications of this Phylogeny (int). A value of -1
344      * indicates that the number of duplications is unknown.
345      *
346      * @param clean_nh
347      *            set to true for clean NH format
348      */
349     // public void setNumberOfDuplications( int i ) {
350     // if ( i < 0 ) {
351     // _number_of_duplications = -1;
352     // }
353     // else {
354     // _number_of_duplications = i;
355     // }
356     // } // setNumberOfDuplications( int )
357     /**
358      * Returns the first external PhylogenyNode.
359      */
360     public PhylogenyNode getFirstExternalNode() {
361         if ( isEmpty() ) {
362             throw new FailedConditionCheckException( "attempt to obtain first external node of empty phylogeney" );
363         }
364         PhylogenyNode node = getRoot();
365         while ( node.isInternal() ) {
366             node = node.getFirstChildNode();
367         }
368         return node;
369     }
370
371     /**
372      * This calculates the height for rooted, tree-shaped phylogenies. The
373      * height is the longest distance from the root to an external node.
374      *
375      * @return the height for rooted, tree-shaped phylogenies
376      */
377     public double calculateHeight(final boolean take_collapse_into_account) {
378         if ( isEmpty() ) {
379             return 0.0;
380         }
381         return calculateSubtreeHeight( getRoot(), take_collapse_into_account );
382     }
383
384     public Identifier getIdentifier() {
385         return _identifier;
386     }
387
388     /**
389      * Returns the name of this Phylogeny.
390      */
391     public String getName() {
392         return _name;
393     }
394
395     /**
396      * Finds the PhylogenyNode of this Phylogeny which has a matching ID number.
397      * @return PhylogenyNode with matching ID, null if not found
398      */
399     public PhylogenyNode getNode( final long id ) throws NoSuchElementException {
400         if ( isEmpty() ) {
401             throw new NoSuchElementException( "attempt to get node in an empty phylogeny" );
402         }
403         if ( ( getIdToNodeMap() == null ) || getIdToNodeMap().isEmpty() ) {
404             reHashIdToNodeMap();
405         }
406         return getIdToNodeMap().get( id );
407     }
408
409     /**
410      * Returns a PhylogenyNode of this Phylogeny which has a matching name.
411      * Throws an Exception if seqname is not present in this or not unique.
412      *
413      * @param name
414      *            name (String) of PhylogenyNode to find
415      * @return PhylogenyNode with matchin name
416      */
417     public PhylogenyNode getNode( final String name ) {
418         if ( isEmpty() ) {
419             return null;
420         }
421         final List<PhylogenyNode> nodes = getNodes( name );
422         if ( ( nodes == null ) || ( nodes.size() < 1 ) ) {
423             throw new IllegalArgumentException( "node named \"" + name + "\" not found" );
424         }
425         if ( nodes.size() > 1 ) {
426             throw new IllegalArgumentException( "node named \"" + name + "\" not unique" );
427         }
428         return nodes.get( 0 );
429     }
430
431     /**
432      * This is time-inefficient since it runs a iterator each time it is called.
433      *
434      */
435     public int getNodeCount() {
436         if ( isEmpty() ) {
437             return 0;
438         }
439         int c = 0;
440         for( final PhylogenyNodeIterator it = iteratorPreorder(); it.hasNext(); it.next() ) {
441             ++c;
442         }
443         return c;
444     }
445
446     /**
447      * Returns a List with references to all Nodes of this Phylogeny which have
448      * a matching name.
449      *
450      * @param name
451      *            name (String) of Nodes to find
452      * @return Vector of references to Nodes of this Phylogeny with matching
453      *         names
454      * @see #getNodesWithMatchingSpecies(String)
455      */
456     public List<PhylogenyNode> getNodes( final String name ) {
457         if ( isEmpty() ) {
458             return null;
459         }
460         final List<PhylogenyNode> nodes = new ArrayList<PhylogenyNode>();
461         for( final PhylogenyNodeIterator iter = iteratorPreorder(); iter.hasNext(); ) {
462             final PhylogenyNode n = iter.next();
463             if ( n.getName().equals( name ) ) {
464                 nodes.add( n );
465             }
466         }
467         return nodes;
468     }
469
470     public List<PhylogenyNode> getNodesViaSequenceName( final String seq_name ) {
471         if ( isEmpty() ) {
472             return null;
473         }
474         final List<PhylogenyNode> nodes = new ArrayList<PhylogenyNode>();
475         for( final PhylogenyNodeIterator iter = iteratorPreorder(); iter.hasNext(); ) {
476             final PhylogenyNode n = iter.next();
477             if ( n.getNodeData().isHasSequence() && n.getNodeData().getSequence().getName().equals( seq_name ) ) {
478                 nodes.add( n );
479             }
480         }
481         return nodes;
482     }
483
484     public List<PhylogenyNode> getNodesViaSequenceSymbol( final String seq_name ) {
485         if ( isEmpty() ) {
486             return null;
487         }
488         final List<PhylogenyNode> nodes = new ArrayList<PhylogenyNode>();
489         for( final PhylogenyNodeIterator iter = iteratorPreorder(); iter.hasNext(); ) {
490             final PhylogenyNode n = iter.next();
491             if ( n.getNodeData().isHasSequence() && n.getNodeData().getSequence().getSymbol().equals( seq_name ) ) {
492                 nodes.add( n );
493             }
494         }
495         return nodes;
496     }
497
498     public List<PhylogenyNode> getNodesViaGeneName( final String seq_name ) {
499         if ( isEmpty() ) {
500             return null;
501         }
502         final List<PhylogenyNode> nodes = new ArrayList<PhylogenyNode>();
503         for( final PhylogenyNodeIterator iter = iteratorPreorder(); iter.hasNext(); ) {
504             final PhylogenyNode n = iter.next();
505             if ( n.getNodeData().isHasSequence() && n.getNodeData().getSequence().getGeneName().equals( seq_name ) ) {
506                 nodes.add( n );
507             }
508         }
509         return nodes;
510     }
511
512     public List<PhylogenyNode> getNodesViaTaxonomyCode( final String taxonomy_code ) {
513         if ( isEmpty() ) {
514             return null;
515         }
516         final List<PhylogenyNode> nodes = new ArrayList<PhylogenyNode>();
517         for( final PhylogenyNodeIterator iter = iteratorPreorder(); iter.hasNext(); ) {
518             final PhylogenyNode n = iter.next();
519             if ( n.getNodeData().isHasTaxonomy()
520                     && n.getNodeData().getTaxonomy().getTaxonomyCode().equals( taxonomy_code ) ) {
521                 nodes.add( n );
522             }
523         }
524         return nodes;
525     }
526
527     /**
528      * Returns a Vector with references to all Nodes of this Phylogeny which
529      * have a matching species name.
530      *
531      * @param specname
532      *            species name (String) of Nodes to find
533      * @return Vector of references to Nodes of this Phylogeny with matching
534      *         species names.
535      * @see #getNodes(String)
536      */
537     public List<PhylogenyNode> getNodesWithMatchingSpecies( final String specname ) {
538         if ( isEmpty() ) {
539             return null;
540         }
541         final List<PhylogenyNode> nodes = new ArrayList<PhylogenyNode>();
542         for( final PhylogenyNodeIterator iter = iteratorPreorder(); iter.hasNext(); ) {
543             final PhylogenyNode n = iter.next();
544             if ( PhylogenyMethods.getSpecies( n ).equals( specname ) ) {
545                 nodes.add( n );
546             }
547         }
548         return nodes;
549     }
550
551     public PhylogenyNode getNodeViaSequenceName( final String seq_name ) {
552         if ( isEmpty() ) {
553             return null;
554         }
555         final List<PhylogenyNode> nodes = getNodesViaSequenceName( seq_name );
556         if ( ( nodes == null ) || ( nodes.size() < 1 ) ) {
557             throw new IllegalArgumentException( "node with sequence named [" + seq_name + "] not found" );
558         }
559         if ( nodes.size() > 1 ) {
560             throw new IllegalArgumentException( "node with sequence named [" + seq_name + "] not unique" );
561         }
562         return nodes.get( 0 );
563     }
564
565     public PhylogenyNode getNodeViaTaxonomyCode( final String taxonomy_code ) {
566         if ( isEmpty() ) {
567             return null;
568         }
569         final List<PhylogenyNode> nodes = getNodesViaTaxonomyCode( taxonomy_code );
570         if ( ( nodes == null ) || ( nodes.size() < 1 ) ) {
571             throw new IllegalArgumentException( "node with taxonomy code \"" + taxonomy_code + "\" not found" );
572         }
573         if ( nodes.size() > 1 ) {
574             throw new IllegalArgumentException( "node with taxonomy code \"" + taxonomy_code + "\" not unique" );
575         }
576         return nodes.get( 0 );
577     }
578
579     public int getNumberOfBranches() {
580         if ( isEmpty() ) {
581             return 0;
582         }
583         int c = 0;
584         for( final PhylogenyNodeIterator iter = iteratorPreorder(); iter.hasNext(); iter.next() ) {
585             ++c;
586         }
587         if ( !isRooted() ) {
588             --c;
589         }
590         return c;
591     }
592
593     public int getNumberOfInternalNodes() {
594         if ( isEmpty() ) {
595             return 0;
596         }
597         int c = 0;
598         for( final PhylogenyNodeIterator iter = iteratorPreorder(); iter.hasNext(); ) {
599             if ( iter.next().isInternal() ) {
600                 ++c;
601             }
602         }
603         if ( !isRooted() ) {
604             --c;
605         }
606         return c;
607     }
608
609     /**
610      * Returns the sum of external Nodes of this Phylogeny (int).
611      */
612     public int getNumberOfExternalNodes() {
613         if ( isEmpty() ) {
614             return 0;
615         }
616         return getExternalNodes().size();
617     }
618
619     /**
620      * Returns all paralogs of the external PhylogenyNode n of this Phylogeny.
621      * paralog are returned as List of node references.
622      * <p>
623      * PRECONDITION: This tree must be binary and rooted, and speciation -
624      * duplication need to be assigned for each of its internal Nodes.
625      * <p>
626      * Returns null if this Phylogeny is empty or if n is internal.
627      * <p>
628      * (Last modified: 11/22/00) Olivier CHABROL :
629      * olivier.chabrol@univ-provence.fr
630      *
631      * @param n
632      *            external PhylogenyNode whose orthologs are to be returned
633      * @return Vector of references to all orthologous Nodes of PhylogenyNode n
634      *         of this Phylogeny, null if this Phylogeny is empty or if n is
635      *         internal
636      */
637     public List<PhylogenyNode> getParalogousNodes( final PhylogenyNode n, final String[] taxonomyCodeRange ) {
638         PhylogenyNode node = n;
639         PhylogenyNode prev = null;
640         final List<PhylogenyNode> v = new ArrayList<PhylogenyNode>();
641         final Map<PhylogenyNode, List<String>> map = new HashMap<PhylogenyNode, List<String>>();
642         getTaxonomyMap( getRoot(), map );
643         if ( !node.isExternal() || isEmpty() ) {
644             return null;
645         }
646         final String searchNodeSpeciesId = PhylogenyMethods.getTaxonomyIdentifier( n );
647         if ( !node.isExternal() || isEmpty() ) {
648             return null;
649         }
650         List<String> taxIdList = null;
651         final List<String> taxonomyCodeRangeList = Arrays.asList( taxonomyCodeRange );
652         while ( !node.isRoot() ) {
653             prev = node;
654             node = node.getParent();
655             taxIdList = map.get( node );
656             if ( node.isDuplication() && isContains( taxIdList, taxonomyCodeRangeList ) ) {
657                 if ( node.getChildNode1() == prev ) {
658                     v.addAll( getNodeByTaxonomyID( searchNodeSpeciesId, node.getChildNode2()
659                                                    .getAllExternalDescendants() ) );
660                 }
661                 else {
662                     v.addAll( getNodeByTaxonomyID( searchNodeSpeciesId, node.getChildNode1()
663                                                    .getAllExternalDescendants() ) );
664                 }
665             }
666         }
667         return v;
668     }
669
670     public Collection<SequenceRelation.SEQUENCE_RELATION_TYPE> getRelevantSequenceRelationTypes() {
671         if ( _relevant_sequence_relation_types == null ) {
672             _relevant_sequence_relation_types = new Vector<SEQUENCE_RELATION_TYPE>();
673         }
674         return _relevant_sequence_relation_types;
675     }
676
677     /**
678      * Returns the root PhylogenyNode of this Phylogeny.
679      */
680     public PhylogenyNode getRoot() {
681         return _root;
682     }
683
684     public Collection<Sequence> getSequenceRelationQueries() {
685         return _sequenceRelationQueries;
686     }
687
688     public String getType() {
689         return _type;
690     }
691
692     /**
693      * Deletes this Phylogeny.
694      */
695     public void init() {
696         _root = null;
697         _rooted = false;
698         _name = "";
699         _description = "";
700         _type = "";
701         _distance_unit = "";
702         _id_to_node_map = null;
703         _confidence = null;
704         _identifier = null;
705         _rerootable = true;
706         setAllowMultipleParents( Phylogeny.ALLOW_MULTIPLE_PARENTS_DEFAULT );
707     }
708
709     /**
710      * Returns whether this is a completely binary tree (i.e. all internal nodes
711      * are bifurcations).
712      *
713      */
714     public boolean isCompletelyBinary() {
715         if ( isEmpty() ) {
716             return false;
717         }
718         for( final PhylogenyNodeIterator iter = iteratorPreorder(); iter.hasNext(); ) {
719             final PhylogenyNode node = iter.next();
720             if ( node.isInternal() && ( node.getNumberOfDescendants() != 2 ) ) {
721                 return false;
722             }
723         }
724         return true;
725     }
726
727     /**
728      * Checks whether a Phylogeny object is deleted (or empty).
729      *
730      * @return true if the tree is deleted (or empty), false otherwise
731      */
732     public boolean isEmpty() {
733         return ( getRoot() == null );
734     }
735
736     public boolean isRerootable() {
737         return _rerootable;
738     }
739
740     /**
741      * Returns true is this Phylogeny is rooted.
742      */
743     public boolean isRooted() {
744         return _rooted;
745     } // isRooted()
746
747     public boolean isTree() {
748         return true;
749     }
750
751     public PhylogenyNodeIterator iteratorExternalForward() {
752         return new ExternalForwardIterator( this );
753     }
754
755     public PhylogenyNodeIterator iteratorLevelOrder() {
756         return new LevelOrderTreeIterator( this );
757     }
758
759     public PhylogenyNodeIterator iteratorPostorder() {
760         return new PostorderTreeIterator( this );
761     }
762
763     public PhylogenyNodeIterator iteratorPreorder() {
764         return new PreorderTreeIterator( this );
765     }
766
767     /**
768      * Resets the ID numbers of the nodes of this Phylogeny in level order,
769      * starting with start_label (for the root). <br>
770      * WARNING. After this method has been called, node IDs are no longer
771      * unique.
772      */
773     public void levelOrderReID() {
774         if ( isEmpty() ) {
775             return;
776         }
777         _id_to_node_map = null;
778         long max = 0;
779         for( final PhylogenyNodeIterator it = iteratorPreorder(); it.hasNext(); ) {
780             final PhylogenyNode node = it.next();
781             if ( node.isRoot() ) {
782                 node.setId( PhylogenyNode.getNodeCount() );
783             }
784             else {
785                 node.setId( node.getParent().getId() + 1 );
786                 if ( node.getId() > max ) {
787                     max = node.getId();
788                 }
789             }
790         }
791         PhylogenyNode.setNodeCount( max + 1 );
792     }
793
794     /**
795      * Prints descriptions of all external Nodes of this Phylogeny to
796      * System.out.
797      */
798     public void printExtNodes() {
799         if ( isEmpty() ) {
800             return;
801         }
802         for( final PhylogenyNodeIterator iter = iteratorExternalForward(); iter.hasNext(); ) {
803             System.out.println( iter.next() + "\n" );
804         }
805     }
806
807     /**
808      * (Re)counts the number of children for each PhylogenyNode of this
809      * Phylogeny. As an example, this method needs to be called after a
810      * Phylogeny has been reRooted and it is to be displayed.
811      *
812      * @param consider_collapsed_nodes
813      *            set to true to take into account collapsed nodes (collapsed
814      *            nodes have 1 child).
815      */
816     public void recalculateNumberOfExternalDescendants( final boolean consider_collapsed_nodes ) {
817         if ( isEmpty() ) {
818             return;
819         }
820         for( final PhylogenyNodeIterator iter = iteratorPostorder(); iter.hasNext(); ) {
821             final PhylogenyNode node = iter.next();
822             if ( node.isExternal() || ( consider_collapsed_nodes && node.isCollapse() ) ) {
823                 node.setSumExtNodes( 1 );
824             }
825             else {
826                 int sum = 0;
827                 for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
828                     sum += node.getChildNode( i ).getNumberOfExternalNodes();
829                 }
830                 node.setSumExtNodes( sum );
831             }
832         }
833     }
834
835     /**
836      * Places the root of this Phylogeny on the parent branch of the
837      * PhylogenyNode with a corresponding ID. The new root is always placed on
838      * the middle of the branch. If the resulting reRooted Phylogeny is to be
839      * used any further, in most cases the following methods have to be called
840      * on the resulting Phylogeny:
841      * <p>
842      * <li>recalculateNumberOfExternalDescendants(boolean)
843      * <li>recalculateAndReset()
844      *
845      * @param id
846      *            ID (int) of PhylogenyNode of this Phylogeny
847      */
848     public void reRoot( final long id ) {
849         reRoot( getNode( id ) );
850     }
851
852     /**
853      * Places the root of this Phylogeny on the parent branch PhylogenyNode n.
854      * The new root is always placed on the middle of the branch.
855      * <p>
856      * If the resulting reRooted Phylogeny is to be used any further, in most
857      * cases the following three methods have to be called on the resulting
858      * Phylogeny:
859      * <ul>
860      * <li>recalculateNumberOfExternalDescendants(boolean) <li>recalculateAndReset()
861      * </ul>
862      * <p>
863      * (Last modified: 10/01/01)
864      *
865      * @param n
866      *            PhylogenyNode of this Phylogeny\
867      */
868     public void reRoot( final PhylogenyNode n ) {
869         reRoot( n, -1 );
870     }
871
872     public void reRoot( final PhylogenyNode n, final double distance_n_to_parent ) {
873         if ( isEmpty() || ( getNumberOfExternalNodes() < 2 ) ) {
874             return;
875         }
876         setRooted( true );
877         if ( n.isRoot() ) {
878             return;
879         }
880         else if ( n.getParent().isRoot() ) {
881             if ( ( n.getParent().getNumberOfDescendants() == 2 ) && ( distance_n_to_parent >= 0 ) ) {
882                 final double d = n.getParent().getChildNode1().getDistanceToParent()
883                         + n.getParent().getChildNode2().getDistanceToParent();
884                 PhylogenyNode other;
885                 if ( n.getChildNodeIndex() == 0 ) {
886                     other = n.getParent().getChildNode2();
887                 }
888                 else {
889                     other = n.getParent().getChildNode1();
890                 }
891                 n.setDistanceToParent( distance_n_to_parent );
892                 final double dm = d - distance_n_to_parent;
893                 if ( dm >= 0 ) {
894                     other.setDistanceToParent( dm );
895                 }
896                 else {
897                     other.setDistanceToParent( 0 );
898                 }
899             }
900             if ( n.getParent().getNumberOfDescendants() > 2 ) {
901                 final int index = n.getChildNodeIndex();
902                 final double dn = n.getDistanceToParent();
903                 final PhylogenyNode prev_root = getRoot();
904                 prev_root.getDescendants().remove( index );
905                 final PhylogenyNode new_root = new PhylogenyNode();
906                 new_root.setChildNode( 0, n );
907                 new_root.setChildNode( 1, prev_root );
908                 if ( n.getBranchDataDirectly() != null ) {
909                     prev_root.setBranchData( ( BranchData ) n.getBranchDataDirectly().copy() );
910                 }
911                 setRoot( new_root );
912                 if ( distance_n_to_parent >= 0 ) {
913                     n.setDistanceToParent( distance_n_to_parent );
914                     final double d = dn - distance_n_to_parent;
915                     if ( d >= 0 ) {
916                         prev_root.setDistanceToParent( d );
917                     }
918                     else {
919                         prev_root.setDistanceToParent( 0 );
920                     }
921                 }
922                 else {
923                     if ( dn >= 0 ) {
924                         final double d = dn / 2.0;
925                         n.setDistanceToParent( d );
926                         prev_root.setDistanceToParent( d );
927                     }
928                 }
929             }
930         }
931         else {
932             PhylogenyNode a = n;
933             PhylogenyNode b = null;
934             PhylogenyNode c = null;
935             final PhylogenyNode new_root = new PhylogenyNode();
936             double distance1 = 0.0;
937             double distance2 = 0.0;
938             BranchData branch_data_1 = null;
939             BranchData branch_data_2 = null;
940             b = a.getParent();
941             c = b.getParent();
942             new_root.setChildNode( 0, a );
943             new_root.setChildNode( 1, b );
944             distance1 = c.getDistanceToParent();
945             if ( c.getBranchDataDirectly() != null ) {
946                 branch_data_1 = ( BranchData ) c.getBranchDataDirectly().copy();
947             }
948             c.setDistanceToParent( b.getDistanceToParent() );
949             if ( b.getBranchDataDirectly() != null ) {
950                 c.setBranchData( ( BranchData ) b.getBranchDataDirectly().copy() );
951             }
952             if ( a.getBranchDataDirectly() != null ) {
953                 b.setBranchData( ( BranchData ) a.getBranchDataDirectly().copy() );
954             }
955             // New root is always placed in the middle of the branch:
956             if ( a.getDistanceToParent() == PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ) {
957                 b.setDistanceToParent( PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT );
958             }
959             else {
960                 if ( distance_n_to_parent >= 0.0 ) {
961                     final double diff = a.getDistanceToParent() - distance_n_to_parent;
962                     a.setDistanceToParent( distance_n_to_parent );
963                     b.setDistanceToParent( diff >= 0.0 ? diff : 0.0 );
964                 }
965                 else {
966                     final double d = a.getDistanceToParent() / 2.0;
967                     a.setDistanceToParent( d );
968                     b.setDistanceToParent( d );
969                 }
970             }
971             b.setChildNodeOnly( a.getChildNodeIndex( b ), c );
972             // moving to the old root, swapping references:
973             while ( !c.isRoot() ) {
974                 a = b;
975                 b = c;
976                 c = c.getParent();
977                 b.setChildNodeOnly( a.getChildNodeIndex( b ), c );
978                 b.setParent( a );
979                 distance2 = c.getDistanceToParent();
980                 branch_data_2 = c.getBranchDataDirectly();
981                 c.setDistanceToParent( distance1 );
982                 c.setBranchData( branch_data_1 );
983                 distance1 = distance2;
984                 branch_data_1 = branch_data_2;
985             }
986             // removing the old root:
987             if ( c.getNumberOfDescendants() == 2 ) {
988                 final PhylogenyNode node = c.getChildNode( 1 - b.getChildNodeIndex( c ) );
989                 node.setParent( b );
990                 if ( ( c.getDistanceToParent() == PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT )
991                         && ( node.getDistanceToParent() == PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ) ) {
992                     node.setDistanceToParent( PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT );
993                 }
994                 else {
995                     node.setDistanceToParent( ( c.getDistanceToParent() >= 0.0 ? c.getDistanceToParent() : 0.0 )
996                                               + ( node.getDistanceToParent() >= 0.0 ? node.getDistanceToParent() : 0.0 ) );
997                 }
998                 if ( c.getBranchDataDirectly() != null ) {
999                     node.setBranchData( ( BranchData ) c.getBranchDataDirectly().copy() );
1000                 }
1001                 for( int i = 0; i < b.getNumberOfDescendants(); ++i ) {
1002                     if ( b.getChildNode( i ) == c ) {
1003                         b.setChildNodeOnly( i, node );
1004                         break;
1005                     }
1006                 }
1007             }
1008             else {
1009                 c.setParent( b );
1010                 c.removeChildNode( b.getChildNodeIndex( c ) );
1011             }
1012             setRoot( new_root );
1013         }
1014     }
1015
1016     /**
1017      * Sets all Nodes of this Phylogeny to not-collapsed.
1018      * <p>
1019      * In most cases methods adjustNodeCount(false) and recalculateAndReset()
1020      * need to be called after this method has been called.
1021      */
1022     public void setAllNodesToNotCollapse() {
1023         if ( isEmpty() ) {
1024             return;
1025         }
1026         for( final PhylogenyNodeIterator iter = iteratorPreorder(); iter.hasNext(); ) {
1027             final PhylogenyNode node = iter.next();
1028             node.setCollapse( false );
1029         }
1030     }
1031
1032     public void setConfidence( final Confidence confidence ) {
1033         _confidence = confidence;
1034     }
1035
1036     public void setDescription( final String description ) {
1037         _description = description;
1038     }
1039
1040     public void setDistanceUnit( final String _distance_unit ) {
1041         this._distance_unit = _distance_unit;
1042     }
1043
1044     public void setIdentifier( final Identifier identifier ) {
1045         _identifier = identifier;
1046     }
1047
1048     public void setIdToNodeMap( final HashMap<Long, PhylogenyNode> idhash ) {
1049         _id_to_node_map = idhash;
1050     }
1051
1052     /**
1053      * Sets the indicators of all Nodes of this Phylogeny to 0.
1054      */
1055     public void setIndicatorsToZero() {
1056         if ( isEmpty() ) {
1057             return;
1058         }
1059         for( final PhylogenyNodeIterator iter = iteratorPreorder(); iter.hasNext(); ) {
1060             iter.next().setIndicator( ( byte ) 0 );
1061         }
1062     } // setIndicatorsToZero()
1063
1064     /**
1065      * Sets the name of this Phylogeny to s.
1066      */
1067     public void setName( final String s ) {
1068         _name = s;
1069     }
1070
1071     public void setRelevantSequenceRelationTypes( final Collection<SequenceRelation.SEQUENCE_RELATION_TYPE> types ) {
1072         _relevant_sequence_relation_types = types;
1073     }
1074
1075     public void setRerootable( final boolean rerootable ) {
1076         _rerootable = rerootable;
1077     }
1078
1079     public void setRoot( final PhylogenyNode n ) {
1080         _root = n;
1081     }
1082
1083     /**
1084      * Sets whether this Phylogeny is rooted or not.
1085      */
1086     public void setRooted( final boolean b ) {
1087         _rooted = b;
1088     } // setRooted( boolean )
1089
1090     public void setSequenceRelationQueries( final Collection<Sequence> sequencesByName ) {
1091         _sequenceRelationQueries = sequencesByName;
1092     }
1093
1094     public void setType( final String type ) {
1095         _type = type;
1096     }
1097
1098     public String toNewHampshire() {
1099         return toNewHampshire( NH_CONVERSION_SUPPORT_VALUE_STYLE.NONE );
1100     }
1101
1102     public String toNewHampshire( final NH_CONVERSION_SUPPORT_VALUE_STYLE nh_conversion_support_style ) {
1103         try {
1104             return new PhylogenyWriter().toNewHampshire( this, true, nh_conversion_support_style ).toString();
1105         }
1106         catch ( final IOException e ) {
1107             throw new Error( "this should not have happend: " + e.getMessage() );
1108         }
1109     }
1110
1111     public String toNewHampshireX() {
1112         try {
1113             return new PhylogenyWriter().toNewHampshireX( this ).toString();
1114         }
1115         catch ( final IOException e ) {
1116             throw new Error( "this should not have happend: " + e.getMessage() );
1117         }
1118     }
1119
1120     public String toNexus() {
1121         return toNexus( NH_CONVERSION_SUPPORT_VALUE_STYLE.NONE );
1122     }
1123
1124     public String toNexus( final NH_CONVERSION_SUPPORT_VALUE_STYLE svs ) {
1125         try {
1126             return new PhylogenyWriter().toNexus( this, svs ).toString();
1127         }
1128         catch ( final IOException e ) {
1129             throw new Error( "this should not have happend: " + e.getMessage() );
1130         }
1131     }
1132
1133     public String toPhyloXML( final int phyloxml_level ) {
1134         try {
1135             return new PhylogenyWriter().toPhyloXML( this, phyloxml_level ).toString();
1136         }
1137         catch ( final IOException e ) {
1138             throw new Error( "this should not have happend: " + e.getMessage() );
1139         }
1140     }
1141
1142     // ---------------------------------------------------------
1143     // Writing of Phylogeny to Strings
1144     // ---------------------------------------------------------
1145     /**
1146      * Converts this Phylogeny to a New Hampshire X (String) representation.
1147      *
1148      * @return New Hampshire X (String) representation of this
1149      * @see #toNewHampshireX()
1150      */
1151     @Override
1152     public String toString() {
1153         return toNewHampshireX();
1154     }
1155
1156     /**
1157      * Removes the root PhylogenyNode this Phylogeny.
1158      */
1159     public void unRoot() throws RuntimeException {
1160         if ( !isTree() ) {
1161             throw new FailedConditionCheckException( "Attempt to unroot a phylogeny which is not tree-like." );
1162         }
1163         if ( isEmpty() ) {
1164             return;
1165         }
1166         setIndicatorsToZero();
1167         if ( !isRooted() || ( getNumberOfExternalNodes() <= 1 ) ) {
1168             return;
1169         }
1170         setRooted( false );
1171         return;
1172     } // unRoot()
1173
1174     private HashMap<Long, PhylogenyNode> getIdToNodeMap() {
1175         return _id_to_node_map;
1176     }
1177
1178     /**
1179      * Return Node by TaxonomyId Olivier CHABROL :
1180      * olivier.chabrol@univ-provence.fr
1181      *
1182      * @param taxonomyID
1183      *            search taxonomy identifier
1184      * @param nodes
1185      *            sublist node to search
1186      * @return List node with the same taxonomy identifier
1187      */
1188     private List<PhylogenyNode> getNodeByTaxonomyID( final String taxonomyID, final List<PhylogenyNode> nodes ) {
1189         final List<PhylogenyNode> retour = new ArrayList<PhylogenyNode>();
1190         for( final PhylogenyNode node : nodes ) {
1191             if ( taxonomyID.equals( PhylogenyMethods.getTaxonomyIdentifier( node ) ) ) {
1192                 retour.add( node );
1193             }
1194         }
1195         return retour;
1196     }
1197
1198     /**
1199      * List all species contains in all leaf under a node Olivier CHABROL :
1200      * olivier.chabrol@univ-provence.fr
1201      *
1202      * @param node
1203      *            PhylogenyNode whose sub node species are returned
1204      * @return species contains in all leaf under the param node
1205      */
1206     private List<String> getSubNodeTaxonomy( final PhylogenyNode node ) {
1207         final List<String> taxonomyList = new ArrayList<String>();
1208         final List<PhylogenyNode> childs = node.getAllExternalDescendants();
1209         String speciesId = null;
1210         for( final PhylogenyNode phylogenyNode : childs ) {
1211             // taxId = new Long(phylogenyNode.getTaxonomyID());
1212             speciesId = PhylogenyMethods.getTaxonomyIdentifier( phylogenyNode );
1213             if ( !taxonomyList.contains( speciesId ) ) {
1214                 taxonomyList.add( speciesId );
1215             }
1216         }
1217         return taxonomyList;
1218     }
1219
1220     /**
1221      * Create a map [<PhylogenyNode, List<String>], the list contains the
1222      * species contains in all leaf under phylogeny node Olivier CHABROL :
1223      * olivier.chabrol@univ-provence.fr
1224      *
1225      * @param node
1226      *            the tree root node
1227      * @param map
1228      *            map to fill
1229      */
1230     private void getTaxonomyMap( final PhylogenyNode node, final Map<PhylogenyNode, List<String>> map ) {
1231         // node is leaf
1232         if ( node.isExternal() ) {
1233             return;
1234         }
1235         map.put( node, getSubNodeTaxonomy( node ) );
1236         getTaxonomyMap( node.getChildNode1(), map );
1237         getTaxonomyMap( node.getChildNode2(), map );
1238     }
1239
1240     private boolean isAllowMultipleParents() {
1241         return _allow_multiple_parents;
1242     }
1243
1244     /**
1245      * Util method to check if all element of a list is contains in the
1246      * rangeList. Olivier CHABROL : olivier.chabrol@univ-provence.fr
1247      *
1248      * @param list
1249      *            list to be check
1250      * @param rangeList
1251      *            the range list to compare
1252      * @return <code>true</code> if all param list element are contains in param
1253      *         rangeList, <code>false</code> otherwise.
1254      */
1255     private boolean isContains( final List<String> list, final List<String> rangeList ) {
1256         if ( list.size() > rangeList.size() ) {
1257             return false;
1258         }
1259         String l = null;
1260         for( final Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
1261             l = iterator.next();
1262             if ( !rangeList.contains( l ) ) {
1263                 return false;
1264             }
1265         }
1266         return true;
1267     }
1268
1269     /**
1270      * Hashes the ID number of each PhylogenyNode of this Phylogeny to its
1271      * corresponding PhylogenyNode, in order to make method getNode( id ) run in
1272      * constant time. Important: The user is responsible for calling this method
1273      * (again) after this Phylogeny has been changed/created/renumbered.
1274      */
1275     private void reHashIdToNodeMap() {
1276         if ( isEmpty() ) {
1277             return;
1278         }
1279         setIdToNodeMap( new HashMap<Long, PhylogenyNode>() );
1280         for( final PhylogenyNodeIterator iter = iteratorPreorder(); iter.hasNext(); ) {
1281             final PhylogenyNode node = iter.next();
1282             getIdToNodeMap().put( node.getId(), node );
1283         }
1284     }
1285
1286     private void setAllowMultipleParents( final boolean allow_multiple_parents ) {
1287         _allow_multiple_parents = allow_multiple_parents;
1288     }
1289 }