3858ea97333502c1ede1ac23cc4b61e80a3fc376
[jalview.git] / forester / java / src / org / forester / phylogeny / PhylogenyNode.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: www.phylosoft.org/forester
27
28 package org.forester.phylogeny;
29
30 import java.util.ArrayList;
31 import java.util.List;
32
33 import org.forester.io.parsers.nhx.NHXFormatException;
34 import org.forester.io.parsers.nhx.NHXParser;
35 import org.forester.io.parsers.util.PhylogenyParserException;
36 import org.forester.phylogeny.data.BranchData;
37 import org.forester.phylogeny.data.NodeData;
38 import org.forester.phylogeny.iterators.ChildNodeIteratorForward;
39 import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
40 import org.forester.phylogeny.iterators.PreorderTreeIterator;
41 import org.forester.util.ForesterUtil;
42
43 public class PhylogenyNode implements PhylogenyNodeI, Comparable<PhylogenyNode> {
44
45     /** Value of -99.0 is used as default value. */
46     public final static double       DISTANCE_DEFAULT = -1024.0;
47     private static int               _node_count      = 0;
48     private byte                     _indicator;
49     private int                      _id;
50     private int                      _sum_ext_nodes;
51     private float                    _x;
52     private float                    _y;
53     private double                   _distance_parent;
54     private boolean                  _collapse;
55     private PhylogenyNode            _parent;
56     private PhylogenyNode            _link;
57     private ArrayList<PhylogenyNode> _descendants;
58     private NodeData                 _node_data;
59     private BranchData               _branch_data;
60     private float                    _x_secondary;
61     private float                    _y_secondary;
62
63     /**
64      * Default constructor for PhylogenyNode.
65      */
66     public PhylogenyNode() {
67         init();
68         setId( PhylogenyNode.getNodeCount() );
69         PhylogenyNode.increaseNodeCount();
70         setSumExtNodes( 1 ); // For ext node, this number is 1 (not 0!!)
71     }
72
73     public PhylogenyNode( final String nhx ) throws NHXFormatException {
74         this( nhx, ForesterUtil.TAXONOMY_EXTRACTION.NO );
75     }
76
77     public PhylogenyNode( final String nhx, final ForesterUtil.TAXONOMY_EXTRACTION taxonomy_extraction )
78             throws NHXFormatException {
79         init();
80         NHXParser.parseNHX( nhx, this, taxonomy_extraction, false );
81         setId( PhylogenyNode.getNodeCount() );
82         PhylogenyNode.increaseNodeCount();
83         setSumExtNodes( 1 ); // For ext node, this number is 1 (not 0!!)
84     }
85
86     /**
87      * Constructor for PhylogenyNode.
88      * <p>
89      * 
90      * @param s
91      *            String representing one PhylogenyNode in New Hampshire (NH) or
92      *            New Hampshire X (NHX) format.
93      * @throws NHXFormatException
94      * @throws PhylogenyParserException
95      */
96     public PhylogenyNode( final String nhx,
97                           final ForesterUtil.TAXONOMY_EXTRACTION taxonomy_extraction,
98                           final boolean replace_underscores ) throws NHXFormatException {
99         init();
100         NHXParser.parseNHX( nhx, this, taxonomy_extraction, replace_underscores );
101         setId( PhylogenyNode.getNodeCount() );
102         PhylogenyNode.increaseNodeCount();
103         setSumExtNodes( 1 ); // For ext node, this number is 1 (not 0!!)
104     }
105
106     /**
107      * Adds PhylogenyNode n to the list of child nodes and sets the _parent of n
108      * to this.
109      * 
110      * @param n
111      *            the PhylogenyNode to add
112      */
113     final public void addAsChild( final PhylogenyNodeI node ) {
114         final PhylogenyNode n = ( PhylogenyNode ) node;
115         addChildNode( n );
116         n.setParent( this );
117     }
118
119     /**
120      * Adds PhylogenyNode n to the list of child nodes. But does NOT set the
121      * _parent of n to this.
122      * 
123      * @see addAsChild( PhylogenyNode n )
124      * @param n
125      *            the PhylogenyNode to add
126      */
127     final private void addChildNode( final PhylogenyNode child ) {
128         getDescendants().add( child );
129     }
130
131     final public int compareTo( final PhylogenyNode o ) {
132         final PhylogenyNode n = o;
133         if ( ( getName() == null ) || ( n.getName() == null ) ) {
134             return 0;
135         }
136         return getName().compareTo( n.getName() );
137     }
138
139     // ---------------------------------------------------------
140     // Copy and delete Nodes, copy subtress
141     // ---------------------------------------------------------
142     /**
143      * Returns a new PhylogenyNode which has its data copied from this
144      * PhylogenyNode. Links to the other Nodes in the same Phylogeny are NOT
145      * copied (e.g. _link to _parent). Field "_link" IS copied.
146      * 
147      * @see #getLink() 
148      */
149     final public PhylogenyNode copyNodeData() {
150         final PhylogenyNode node = new PhylogenyNode();
151         PhylogenyNode.decreaseNodeCount();
152         node._id = _id;
153         node._sum_ext_nodes = _sum_ext_nodes;
154         node._indicator = _indicator;
155         node._x = _x;
156         node._y = _y;
157         node._distance_parent = _distance_parent;
158         node._collapse = _collapse;
159         node._link = _link;
160         if ( _node_data != null ) {
161             node._node_data = ( NodeData ) _node_data.copy();
162         }
163         if ( _branch_data != null ) {
164             node._branch_data = ( BranchData ) _branch_data.copy();
165         }
166         return node;
167     }
168
169     /**
170      * Returns a new PhylogenyNode which has the same data as this
171      * PhylogenyNode. Links to the other Nodes in the same Phylogeny are NOT
172      * copied (e.g. _link to _parent). Field "_link" IS copied.
173      * 
174      * @see #getLink() 
175      */
176     final public PhylogenyNode copyNodeDataShallow() {
177         final PhylogenyNode node = new PhylogenyNode();
178         PhylogenyNode.decreaseNodeCount();
179         node._id = _id;
180         node._sum_ext_nodes = _sum_ext_nodes;
181         node._indicator = _indicator;
182         node._x = _x;
183         node._y = _y;
184         node._distance_parent = _distance_parent;
185         node._collapse = _collapse;
186         node._link = _link;
187         node._node_data = _node_data;
188         node._branch_data = _branch_data;
189         return node;
190     }
191
192     @Override
193     /**
194      * Based on node name, sequence, and taxonomy.
195      * 
196      * 
197      */
198     final public boolean equals( final Object o ) {
199         if ( this == o ) {
200             return true;
201         }
202         else if ( o == null ) {
203             return false;
204         }
205         else if ( o.getClass() != this.getClass() ) {
206             throw new IllegalArgumentException( "attempt to check [" + this.getClass() + "] equality to " + o + " ["
207                     + o.getClass() + "]" );
208         }
209         else {
210             final PhylogenyNode other = ( PhylogenyNode ) o;
211             if ( !getName().equals( other.getName() ) ) {
212                 return false;
213             }
214             final NodeData this_data = getNodeData();
215             final NodeData other_data = other.getNodeData();
216             if ( ( this_data.isHasSequence() && other_data.isHasSequence() )
217                     && ( this_data.isHasTaxonomy() && other_data.isHasTaxonomy() ) ) {
218                 return ( this_data.getTaxonomy().isEqual( other_data.getTaxonomy() ) && this_data.getSequence()
219                         .isEqual( other_data.getSequence() ) );
220             }
221             else if ( this_data.isHasSequence() && other_data.isHasSequence() ) {
222                 return ( this_data.getSequence().isEqual( other_data.getSequence() ) );
223             }
224             else if ( this_data.isHasTaxonomy() && other_data.isHasTaxonomy() ) {
225                 return ( this_data.getTaxonomy().isEqual( other_data.getTaxonomy() ) );
226             }
227             else if ( getName().length() > 0 ) {
228                 // Node name is not empty, and equal.
229                 return true;
230             }
231             else {
232                 return false;
233             }
234         }
235     }
236
237     // ---------------------------------------------------------
238     // Obtaining of Nodes
239     // ---------------------------------------------------------
240     /**
241      * Returns a List containing references to all external children of this
242      * PhylogenyNode.
243      * 
244      * @return List of references to external Nodes
245      */
246     final public List<PhylogenyNode> getAllExternalDescendants() {
247         final List<PhylogenyNode> nodes = new ArrayList<PhylogenyNode>();
248         if ( isExternal() ) {
249             nodes.add( this );
250             return nodes;
251         }
252         PhylogenyNode node1 = this;
253         while ( !node1.isExternal() ) {
254             node1 = node1.getFirstChildNode();
255         }
256         PhylogenyNode node2 = this;
257         while ( !node2.isExternal() ) {
258             node2 = node2.getLastChildNode();
259         }
260         while ( node1 != node2 ) {
261             nodes.add( node1 );
262             node1 = node1.getNextExternalNode();
263         }
264         nodes.add( node2 );
265         return nodes;
266     }
267
268     /**
269      * Returns a List containing references to all names of the external
270      * children of this PhylogenyNode.
271      * 
272      * @return List of references to names of external Nodes
273      */
274     final public List<String> getAllExternalDescendantsNames() {
275         final List<PhylogenyNode> c = getAllExternalDescendants();
276         final List<String> n = new ArrayList<String>( c.size() );
277         for( final PhylogenyNode phylogenyNode : c ) {
278             n.add( phylogenyNode.getName() );
279         }
280         return n;
281     }
282
283     final public BranchData getBranchData() {
284         if ( _branch_data == null ) {
285             _branch_data = new BranchData();
286         }
287         return _branch_data;
288     }
289
290     final BranchData getBranchDataDirectly() {
291         return _branch_data;
292     }
293
294     /**
295      * This return child node n of this node.
296      * 
297      * @param n
298      *            the index of the child to get
299      * @return the child node with index n
300      * @throws IllegalArgumentException
301      *             if n is out of bounds
302      */
303     final public PhylogenyNode getChildNode( final int i ) {
304         if ( isExternal() ) {
305             throw new UnsupportedOperationException( "attempt to get the child node of an external node." );
306         }
307         if ( ( i >= getNumberOfDescendants() ) || ( i < 0 ) ) {
308             throw new IllegalArgumentException( "attempt to get child node " + i + " of a node with "
309                     + getNumberOfDescendants() + " child nodes" );
310         }
311         return getDescendants().get( i );
312     }
313
314     /**
315      * Convenience method. Returns the first child PhylogenyNode of this
316      * PhylogenyNode.
317      */
318     final public PhylogenyNode getChildNode1() {
319         return getChildNode( 0 );
320     }
321
322     /**
323      * Convenience method. Returns the second child PhylogenyNode of this
324      * PhylogenyNode.
325      * <p>
326      * [last modified May 18, 2005 by CMZ]
327      */
328     final public PhylogenyNode getChildNode2() {
329         return getChildNode( 1 );
330     }
331
332     /**
333      * This gets the child node index of this node.
334      * <p>
335      * 
336      * @return the child node index of this node
337      * @throws UnsupportedOperationException
338      *             if this node is a root node
339      */
340     final public int getChildNodeIndex() {
341         return getChildNodeIndex( getParent() );
342     }
343
344     /**
345      * This gets the child node index of this node, given that parent is its
346      * parent
347      * <p>
348      * [last modified Aug 14, 2006 by CMZ]
349      * 
350      * @return the child node index of this node
351      * @throws UnsupportedOperationException
352      *             if this node is a root node
353      */
354     final public int getChildNodeIndex( final PhylogenyNode parent ) {
355         if ( isRoot() ) {
356             throw new UnsupportedOperationException( "Cannot get the child index for a root node." );
357         }
358         for( int i = 0; i < parent.getNumberOfDescendants(); ++i ) {
359             if ( parent.getChildNode( i ) == this ) {
360                 return i;
361             }
362         }
363         throw new RuntimeException( "Unexpected exception: Could not determine the child index for node: " + this );
364     }
365
366     final public List<PhylogenyNode> getDescendants() {
367         return _descendants;
368     }
369
370     /**
371      * Returns the length of the branch leading to the _parent of this
372      * PhylogenyNode (double).
373      */
374     final public double getDistanceToParent() {
375         return _distance_parent;
376     }
377
378     /**
379      * Convenience method. Returns the first child node of this node.
380      * <p>
381      * [last modified May 18, 2005 by CMZ]
382      * 
383      * @return the first child node of this node
384      */
385     public final PhylogenyNode getFirstChildNode() {
386         return getChildNode( 0 );
387     }
388
389     /**
390      * Returns the _indicator value of this PhylogenyNode.
391      */
392     public final byte getIndicator() {
393         return _indicator;
394     }
395
396     /**
397      * Convenience method. Returns the last child node of this node.
398      * <p>
399      * [last modified May 18, 2005 by CMZ]
400      * 
401      * @return the last child node of this node
402      */
403     public final PhylogenyNode getLastChildNode() {
404         return getChildNode( getNumberOfDescendants() - 1 );
405     }
406
407     /**
408      * Returns a refernce to the linked PhylogenyNode of this PhylogenyNode.
409      * Currently, this method is only used for the speciation-_duplication
410      * assignment algorithms.
411      */
412     public final PhylogenyNode getLink() {
413         return _link;
414     }
415
416     /**
417      * Returns a refernce to the next external PhylogenyNode of this
418      * PhylogenyNode. TODO should be in Phylogeny. Returns null if no next
419      * external node is available.
420      */
421     public final PhylogenyNode getNextExternalNode() {
422         if ( isInternal() ) {
423             throw new UnsupportedOperationException( "attempt to get next external node of an internal node" );
424         }
425         else if ( isLastExternalNode() ) {
426             return null;
427         }
428         int index = getChildNodeIndex();
429         PhylogenyNode previous_node = this;
430         PhylogenyNode current_node = getParent();
431         while ( !current_node.isRoot()
432                 && ( ( current_node.getNumberOfDescendants() == 1 ) || previous_node.isLastChildNode() ) ) {
433             index = current_node.getChildNodeIndex();
434             previous_node = current_node;
435             current_node = current_node.getParent();
436         }
437         current_node = current_node.getChildNode( index + 1 );
438         while ( current_node.isInternal() ) {
439             current_node = current_node.getFirstChildNode();
440         }
441         return current_node;
442     }
443
444     public final NodeData getNodeData() {
445         if ( _node_data == null ) {
446             _node_data = new NodeData();
447         }
448         return _node_data;
449     }
450
451     final NodeData getNodeDataDirectly() {
452         return _node_data;
453     }
454
455     // ---------------------------------------------------------
456     // Set and get methods for Nodes
457     // ---------------------------------------------------------
458     /**
459      * Returns the ID (int) of this PhylogenyNode.
460      */
461     final public int getId() {
462         return _id;
463     }
464
465     /**
466      * Returns the name of this node.
467      */
468     final public String getName() {
469         return getNodeData().getNodeName();
470     }
471
472     final public int getNumberOfDescendants() {
473         return _descendants.size();
474     }
475
476     /**
477      * Returns the total number of external Nodes originating from this
478      * PhylogenyNode (int).
479      */
480     final public int getNumberOfExternalNodes() {
481         return _sum_ext_nodes;
482     }
483
484     final public int getNumberOfParents() {
485         return 1;
486     }
487
488     /**
489      * Returns a refernce to the parent PhylogenyNode of this PhylogenyNode.
490      */
491     final public PhylogenyNode getParent() {
492         return _parent;
493     }
494
495     /**
496      * Returns a refernce to the next external PhylogenyNode of this
497      * PhylogenyNode. TODO should be in Phylogeny. Returns null if no next
498      * external node is available.
499      */
500     final public PhylogenyNode getPreviousExternalNode() {
501         if ( isInternal() ) {
502             throw new UnsupportedOperationException( "Cannot get the previous external node for an internal node." );
503         }
504         else if ( isRoot() /* TODO && tree is rooted */) {
505             throw new UnsupportedOperationException( "Cannot get the previous external node for a root node." );
506         }
507         else if ( isFirstExternalNode() ) {
508             throw new UnsupportedOperationException( "Attempt to get previous external node of the first external node." );
509         }
510         int index = getChildNodeIndex();
511         PhylogenyNode previous_node = this;
512         PhylogenyNode current_node = getParent();
513         while ( !current_node.isRoot()
514                 && ( ( current_node.getNumberOfDescendants() == 1 ) || previous_node.isFirstChildNode() ) ) {
515             index = current_node.getChildNodeIndex();
516             previous_node = current_node;
517             current_node = current_node.getParent();
518         }
519         current_node = current_node.getChildNode( index - 1 );
520         while ( current_node.isInternal() ) {
521             current_node = current_node.getLastChildNode();
522         }
523         return current_node;
524     }
525
526     /**
527      * Used for drawing of Trees.
528      */
529     final public float getXcoord() {
530         return _x;
531     }
532
533     final public float getXSecondary() {
534         return _x_secondary;
535     }
536
537     /**
538      * Used for drawing of Trees.
539      */
540     final public float getYcoord() {
541         return _y;
542     }
543
544     final public float getYSecondary() {
545         return _y_secondary;
546     }
547
548     @Override
549     final public int hashCode() {
550         final NodeData data = getNodeData();
551         if ( ( getName().length() < 1 ) && !data.isHasSequence() && !data.isHasTaxonomy() ) {
552             return super.hashCode();
553         }
554         int result = getName().hashCode();
555         if ( data.isHasSequence() ) {
556             result ^= data.getSequence().hashCode();
557         }
558         if ( data.isHasTaxonomy() ) {
559             result ^= data.getTaxonomy().hashCode();
560         }
561         return result;
562     }
563
564     final private void init() {
565         _descendants = new ArrayList<PhylogenyNode>();
566         _parent = null;
567         _id = 0;
568         initializeData();
569     }
570
571     /**
572      * Deletes data of this PhylogenyNode. Links to the other Nodes in the
573      * Phylogeny, the ID and the sum of external nodes are NOT deleted. Field
574      * "_link" (_link to Nodes in other Phylogeny) IS deleted.
575      * 
576      * @see #getLink() (Last modified: 12/20/03)
577      */
578     final public void initializeData() {
579         _indicator = 0;
580         _x = 0;
581         _y = 0;
582         //_node_name = "";
583         _distance_parent = PhylogenyNode.DISTANCE_DEFAULT;
584         _collapse = false;
585         _link = null;
586         _branch_data = null;
587         _node_data = null;
588     }
589
590     /**
591      * Returns whether this PhylogenyNode should be drawn as collapsed.
592      */
593     final public boolean isCollapse() {
594         return _collapse;
595     }
596
597     /**
598      * Returns true if this PhylogenyNode represents a _duplication event, false
599      * otherwise.
600      */
601     final public boolean isDuplication() {
602         return getNodeData().isHasEvent() && getNodeData().getEvent().isDuplication();
603     }
604
605     /**
606      * Checks whether this PhylogenyNode is external (tip).
607      * 
608      * @return true if this PhylogenyNode is external, false otherwise
609      */
610     final public boolean isExternal() {
611         return ( getNumberOfDescendants() < 1 );
612     }
613
614     /**
615      * DOCUMENT ME!
616      * 
617      * @return DOCUMENT ME!
618      */
619     final public boolean isFirstChildNode() {
620         if ( isRoot() /* and tree is rooted TODO */) {
621             throw new UnsupportedOperationException( "Cannot determine whether the root is the first child node of its _parent." );
622         }
623         return ( getChildNodeIndex() == 0 );
624     }
625
626     /**
627      * DOCUMENT ME!
628      * 
629      * @return DOCUMENT ME!
630      */
631     final public boolean isFirstExternalNode() {
632         if ( isInternal() ) {
633             return false;
634         }
635         PhylogenyNode node = this;
636         while ( !node.isRoot() ) {
637             if ( !node.isFirstChildNode() ) {
638                 return false;
639             }
640             node = node.getParent();
641         }
642         return true;
643     }
644
645     /**
646      * Returns whether a _duplication or speciation event has been assigned for
647      * this PhylogenyNode.
648      */
649     final public boolean isHasAssignedEvent() {
650         if ( !getNodeData().isHasEvent() ) {
651             return false;
652         }
653         if ( ( getNodeData().getEvent() ).isUnassigned() ) {
654             return false;
655         }
656         return true;
657     }
658
659     /**
660      * Checks whether this PhylogenyNode is internal (tip).
661      * 
662      * @return true if this PhylogenyNode is external, false otherwise
663      */
664     final public boolean isInternal() {
665         return ( !isExternal() );
666     }
667
668     /**
669      * Returns true if this node is the last child node of its _parent.
670      * <p>
671      * [last modified June 01, 2005 by CMZ]
672      * 
673      * @return true if this node is the last child node of its _parent, false
674      *         otherwise
675      */
676     final public boolean isLastChildNode() {
677         if ( isRoot() /* and tree is rooted TODO */) {
678             throw new UnsupportedOperationException( "Cannot determine whether the root is the last child node of its _parent." );
679         }
680         return ( getChildNodeIndex() == ( getParent().getNumberOfDescendants() - 1 ) );
681     }
682
683     /**
684      * DOCUMENT ME!
685      * 
686      * @return DOCUMENT ME!
687      */
688     final public boolean isLastExternalNode() {
689         if ( isInternal() ) {
690             return false;
691         }
692         PhylogenyNode node = this;
693         while ( !node.isRoot() ) {
694             if ( !node.isLastChildNode() ) {
695                 return false;
696             }
697             node = node.getParent();
698         }
699         return true;
700     }
701
702     /**
703      * Checks whether this PhylogenyNode is a root.
704      * 
705      * @return true if this PhylogenyNode is the root, false otherwise
706      */
707     final public boolean isRoot() {
708         return _parent == null;
709     }
710
711     final public boolean isSpeciation() {
712         return getNodeData().isHasEvent() && getNodeData().getEvent().isSpeciation();
713     }
714
715     // ---------------------------------------------------------
716     // Iterator
717     // ---------------------------------------------------------
718     final public PhylogenyNodeIterator iterateChildNodesForward() {
719         return new ChildNodeIteratorForward( this );
720     }
721
722     // ---------------------------------------------------------
723     // Basic printing
724     // ---------------------------------------------------------
725     /**
726      * Prints to the console the subtree originating from this PhylogenyNode in
727      * preorder.
728      */
729     public void preorderPrint() {
730         System.out.println( this + "\n" );
731         if ( isInternal() ) {
732             for( int i = 0; i < getNumberOfDescendants(); ++i ) {
733                 getChildNode( i ).preorderPrint();
734             }
735         }
736     }
737
738     final public void removeChildNode( final int i ) {
739         if ( isExternal() ) {
740             throw new UnsupportedOperationException( "cannot get the child node for a external node." );
741         }
742         if ( ( i >= getNumberOfDescendants() ) || ( i < 0 ) ) {
743             throw new IllegalArgumentException( "attempt to get child node " + i + " of a node with "
744                     + getNumberOfDescendants() + " child nodes." );
745         }
746         getDescendants().remove( i );
747     }
748
749     final public void removeChildNode( final PhylogenyNode remove_me ) {
750         removeChildNode( remove_me.getChildNodeIndex() );
751     }
752
753     final public void setBranchData( final BranchData branch_data ) {
754         _branch_data = branch_data;
755     }
756
757     /**
758      * Sets the first child PhylogenyNode of this PhylogenyNode to n.
759      */
760     final public void setChild1( final PhylogenyNode n ) {
761         setChildNode( 0, n );
762     }
763
764     /**
765      * Sets the second child PhylogenyNode of this PhylogenyNode to n.
766      */
767     final public void setChild2( final PhylogenyNode n ) {
768         setChildNode( 1, n );
769     }
770
771     /**
772      * Inserts PhylogenyNode n at the specified position i into the list of
773      * child nodes. This does not allow null slots in the list of child nodes:
774      * If i is larger than the number of child nodes, n is just added to the
775      * list, not place at index i.
776      * 
777      * @param i
778      *            the index of position where to add the child
779      * @param n
780      *            the PhylogenyNode to add
781      */
782     final public void setChildNode( final int i, final PhylogenyNode node ) {
783         node.setParent( this );
784         if ( getNumberOfDescendants() <= i ) {
785             addChildNode( node );
786         }
787         else {
788             getDescendants().set( i, node );
789         }
790     }
791
792     final void setChildNodeOnly( final int i, final PhylogenyNode node ) {
793         if ( getNumberOfDescendants() <= i ) {
794             addChildNode( node );
795         }
796         else {
797             getDescendants().set( i, node );
798         }
799     }
800
801     /**
802      * Sets whether this PhylogenyNode should be drawn as collapsed.
803      */
804     final public void setCollapse( final boolean b ) {
805         _collapse = b;
806     }
807
808     /**
809      * Sets the length of the branch leading to the _parent of this
810      * PhylogenyNode to double d.
811      */
812     final public void setDistanceToParent( final double d ) {
813         _distance_parent = d;
814     }
815
816     /**
817      * Sets the _indicator value of this PhylogenyNode to i.
818      */
819     final public void setIndicator( final byte i ) {
820         _indicator = i;
821     }
822
823     // --------------------------------------------------------------------
824     // Adjust methods (related to Phylogeny construction and
825     // Phylogeny modification)
826     // --------------------------------------------------------------------
827     /**
828      * Sets the indicators of all the children of this PhylogenyNode to zero.
829      */
830     final void setIndicatorsToZero() {
831         for( final PreorderTreeIterator it = new PreorderTreeIterator( this ); it.hasNext(); ) {
832             it.next().setIndicator( ( byte ) 0 );
833         }
834     }
835
836     /**
837      * Sets the linked PhylogenyNode of this PhylogenyNode to n. Currently, this
838      * method is only used for the speciation-_duplication assignment
839      * algorithms.
840      */
841     final public void setLink( final PhylogenyNode n ) {
842         _link = n;
843     }
844
845     /**
846      * Sets the name of this node.
847      */
848     final public void setName( final String node_name ) {
849         getNodeData().setNodeName( node_name );
850     }
851
852     /**
853      * Sets the Id of this PhylogenyNode to i. In most cases, this number
854      * should not be set to values lower than getNodeCount() -- which this method
855      * does not allow.
856      */
857     synchronized final protected void setId( final int i ) {
858         if ( i < getNodeCount() ) {
859             throw new IllegalArgumentException( "attempt to set node id to a value less than total node count (thus violating the uniqueness of node ids)" );
860         }
861         _id = i;
862     }
863
864     /**
865      * Sets the _parent PhylogenyNode of this PhylogenyNode to n.
866      */
867     final public void setParent( final PhylogenyNode n ) {
868         _parent = n;
869     }
870
871     /**
872      * Sets the total number of external Nodes originating from this
873      * PhylogenyNode to i (int).
874      */
875     final public void setSumExtNodes( final int i ) {
876         if ( i < 0 ) {
877             throw new IllegalArgumentException( "attempt to set sum of external nodes to less than one" );
878         }
879         _sum_ext_nodes = i;
880     }
881
882     /**
883      * Used for drawing of Trees.
884      */
885     final public void setXcoord( final float x ) {
886         _x = x;
887     }
888
889     final public void setXSecondary( final float x_secondary ) {
890         _x_secondary = x_secondary;
891     }
892
893     // -----------
894     /**
895      * Used for drawing of Trees.
896      */
897     final public void setYcoord( final float y ) {
898         _y = y;
899     }
900
901     final public void setYSecondary( final float y_secondary ) {
902         _y_secondary = y_secondary;
903     }
904
905     // ---------------------------------------------------------
906     // Writing of Nodes to Strings
907     // ---------------------------------------------------------
908     final public String toNewHampshire( final boolean simple_nh, final boolean write_distance_to_parent ) {
909         final StringBuilder sb = new StringBuilder();
910         String data = "";
911         if ( !ForesterUtil.isEmpty( getName() ) ) {
912             data = getName();
913         }
914         else if ( getNodeData().isHasTaxonomy() ) {
915             if ( !ForesterUtil.isEmpty( getNodeData().getTaxonomy().getTaxonomyCode() ) ) {
916                 data = getNodeData().getTaxonomy().getTaxonomyCode();
917             }
918             else if ( !ForesterUtil.isEmpty( getNodeData().getTaxonomy().getScientificName() ) ) {
919                 data = getNodeData().getTaxonomy().getScientificName();
920             }
921             else if ( !ForesterUtil.isEmpty( getNodeData().getTaxonomy().getCommonName() ) ) {
922                 data = getNodeData().getTaxonomy().getCommonName();
923             }
924             else if ( getNodeData().getTaxonomy().getTaxonomyCode() != null ) {
925                 data = getNodeData().getTaxonomy().getTaxonomyCode();
926             }
927         }
928         else if ( getNodeData().isHasSequence() ) {
929             if ( !ForesterUtil.isEmpty( getNodeData().getSequence().getName() ) ) {
930                 data = getNodeData().getSequence().getName();
931             }
932         }
933         if ( data.length() > 0 ) {
934             data = ForesterUtil.replaceIllegalNhCharacters( data );
935             if ( simple_nh && ( data.length() > 10 ) ) {
936                 data = data.substring( 0, 11 );
937             }
938             if ( ForesterUtil.isContainsParanthesesableNhCharacter( data ) ) {
939                 sb.append( '\'' );
940                 sb.append( data );
941                 sb.append( '\'' );
942             }
943             else {
944                 sb.append( data );
945             }
946         }
947         if ( ( getDistanceToParent() != PhylogenyNode.DISTANCE_DEFAULT ) && write_distance_to_parent ) {
948             sb.append( ":" );
949             sb.append( getDistanceToParent() );
950         }
951         return sb.toString();
952     }
953
954     /**
955      * Converts this PhylogenyNode to a New Hampshire X (NHX) String
956      * representation.
957      */
958     final public String toNewHampshireX() {
959         final StringBuffer sb = new StringBuffer();
960         final StringBuffer s_nhx = new StringBuffer();
961         if ( !ForesterUtil.isEmpty( getName() ) ) {
962             final String name = ForesterUtil.replaceIllegalNhCharacters( getName() );
963             if ( ForesterUtil.isContainsParanthesesableNhCharacter( name ) ) {
964                 sb.append( '\'' );
965                 sb.append( name );
966                 sb.append( '\'' );
967             }
968             else {
969                 sb.append( name );
970             }
971         }
972         if ( getDistanceToParent() != PhylogenyNode.DISTANCE_DEFAULT ) {
973             sb.append( ":" );
974             sb.append( getDistanceToParent() );
975         }
976         if ( getNodeDataDirectly() != null ) {
977             s_nhx.append( getNodeDataDirectly().toNHX() );
978         }
979         if ( getBranchDataDirectly() != null ) {
980             s_nhx.append( getBranchDataDirectly().toNHX() );
981         }
982         if ( s_nhx.length() > 0 ) {
983             sb.append( "[&&NHX" );
984             sb.append( s_nhx );
985             sb.append( "]" );
986         }
987         return sb.toString();
988     }
989
990     @Override
991     final public String toString() {
992         final StringBuilder sb = new StringBuilder();
993         if ( !ForesterUtil.isEmpty( getName() ) ) {
994             sb.append( getName() );
995             sb.append( " " );
996         }
997         sb.append( "[" );
998         sb.append( getId() );
999         sb.append( "]" );
1000         return sb.toString();
1001     }
1002
1003     /**
1004      * Decreases the total number of all Nodes created so far by one.
1005      */
1006     final static synchronized void decreaseNodeCount() {
1007         --PhylogenyNode._node_count;
1008     }
1009
1010     /**
1011      * Returns the total number of all Nodes created so far.
1012      * 
1013      * @return total number of Nodes (int)
1014      */
1015     synchronized final public static int getNodeCount() {
1016         return PhylogenyNode._node_count;
1017     }
1018
1019     /**
1020      * Increases the total number of all Nodes created so far by one.
1021      */
1022     synchronized final private static void increaseNodeCount() {
1023         ++PhylogenyNode._node_count;
1024     }
1025
1026     /**
1027      * Sets the total number of all Nodes created so far to i (int).
1028      */
1029     synchronized final static void setNodeCount( final int i ) {
1030         PhylogenyNode._node_count = i;
1031     }
1032 }