/*
* FlexibleNode.java
*
* Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard
*
* This file is part of BEAST.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership and licensing.
*
* BEAST is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* BEAST is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with BEAST; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
package dr.evolution.tree;
import dr.evolution.util.Taxon;
import dr.util.Attributable;
import java.util.Iterator;
/**
* A flexible implementation of the Node interface. Can have both node heights
* and node lengths. Note that the height and length are not garenteed to both
* be correct. Which is currently in use and the conversion between them is handled
* by FlexibleTree.
*
* @author Andrew Rambaut
* @author Alexei Drummond
* @version $Id: FlexibleNode.java,v 1.9 2005/06/20 16:06:02 rambaut Exp $
*/
public class FlexibleNode implements NodeRef, Attributable {
/**
* parent node
*/
private FlexibleNode parent;
/**
* number of node
*/
private int nodeNumber;
/**
* height of this node
*/
private double height;
/**
* length of the branch to the parent
*/
private double length;
/**
* instantaneous rate at this node
*/
private double rate;
/**
* the taxon if node is external
*/
private Taxon taxon = null;
//
// Private stuff
//
private FlexibleNode[] child;
/**
* constructor default node
*/
public FlexibleNode() {
parent = null;
child = null;
height = 0.0;
rate = 1.0;
nodeNumber = 0;
}
/**
* constructor default node
*/
public FlexibleNode(Taxon taxon) {
parent = null;
child = null;
height = 0.0;
rate = 1.0;
nodeNumber = 0;
this.taxon = taxon;
}
public FlexibleNode(FlexibleNode node) {
parent = null;
setHeight(node.getHeight());
setLength(node.getLength());
setRate(node.getRate());
setId(node.getId());
setNumber(node.getNumber());
setTaxon(node.getTaxon());
child = null;
for (int i = 0; i < node.getChildCount(); i++) {
addChild(new FlexibleNode(node.getChild(i)));
}
}
/**
* constructor used to clone a node and all children
*/
public FlexibleNode(Tree tree, NodeRef node) {
this(tree, node, false);
// Unbelievable code duplication
// parent = null;
// setHeight(tree.getNodeHeight(node));
// setLength(tree.getBranchLength(node));
// setRate(tree.getNodeRate(node));
// setId(tree.getTaxonId(node.getNumber()));
// setNumber(node.getNumber());
// setTaxon(tree.getNodeTaxon(node));
//
// child = null;
//
// for (int i = 0; i < tree.getChildCount(node); i++) {
// addChild(new FlexibleNode(tree, tree.getChild(node, i)));
// }
}
public FlexibleNode(Tree tree, NodeRef node, boolean copyAttributes) {
parent = null;
setHeight(tree.getNodeHeight(node));
setLength(tree.getBranchLength(node));
setRate(tree.getNodeRate(node));
setId(tree.getTaxonId(node.getNumber()));
setNumber(node.getNumber());
setTaxon(tree.getNodeTaxon(node));
child = null;
for (int i = 0; i < tree.getChildCount(node); i++) {
addChild(new FlexibleNode(tree, tree.getChild(node, i), copyAttributes));
}
if (copyAttributes) {
Iterator iter = tree.getNodeAttributeNames(node);
if (iter != null) {
while (iter.hasNext()) {
String name = (String) iter.next();
this.setAttribute(name, tree.getNodeAttribute(node, name));
}
}
}
}
public FlexibleNode getDeepCopy() {
return new FlexibleNode(this);
}
public FlexibleNode getShallowCopy() {
FlexibleNode copy = new FlexibleNode();
copy.setHeight(getHeight());
copy.setLength(getLength());
copy.setRate(getRate());
copy.setId(getId());
copy.setNumber(getNumber());
copy.setTaxon(getTaxon());
return copy;
}
/**
* Returns the parent node of this node.
*/
public final FlexibleNode getParent() {
return parent;
}
/**
* Set the parent node of this node.
*/
public void setParent(FlexibleNode node) {
parent = node;
}
/**
* Get the height of this node.
*/
public final double getHeight() {
return height;
}
/**
* Set the height of this node.
*/
public final void setHeight(double value) {
height = value;
}
/**
* Get the length of the branch to the node's parent.
*/
public final double getLength() {
return length;
}
/**
* Set the length of the branch to the node's parent.
*/
public final void setLength(double value) {
length = value;
}
/**
* Get the rate at this node.
*/
public final double getRate() {
return rate;
}
/**
* Set the rate at this node.
*/
public final void setRate(double value) {
rate = value;
}
public void setNumber(int n) {
nodeNumber = n;
}
public int getNumber() {
return nodeNumber;
}
public void setTaxon(Taxon taxon) {
this.taxon = taxon;
}
public Taxon getTaxon() {
return taxon;
}
/**
* get child node
*
* @param n number of child
* @return child node
*/
public FlexibleNode getChild(int n) {
return child[n];
}
public boolean hasChild(FlexibleNode node) {
for (int i = 0, n = getChildCount(); i < n; i++) {
if (node == child[i]) return true;
}
return false;
}
/**
* add new child node
*
* @param n new child node
*/
public void addChild(FlexibleNode n) {
insertChild(n, getChildCount());
}
/**
* add new child node (insertion at a specific position)
*
* @param n new child node
* + @param pos position
*/
public void insertChild(FlexibleNode n, int pos) {
int numChildren = getChildCount();
FlexibleNode[] newChild = new FlexibleNode[numChildren + 1];
// AR 28/05/08
// This doesn't work because pos can be zero and (numChildren - pos) can be zero in which case an NullPointerException is thrown.
// We could check for these special cases but we are generally only moving one or two nodes so it is unlikely to be an efficiency
// gain for using arraycopy.
// System.arraycopy(child, 0, newChild, 0, pos);
// newChild[pos] = n;
// System.arraycopy(child, pos, newChild, pos + 1, numChildren - pos);
for (int i = 0; i < pos; i++) {
newChild[i] = child[i];
}
newChild[pos] = n;
for (int i = pos; i < numChildren; i++) {
newChild[i + 1] = child[i];
}
child = newChild;
n.setParent(this);
}
/**
* remove child
*
* @param n child to be removed
*/
public FlexibleNode removeChild(FlexibleNode n) {
int numChildren = getChildCount();
FlexibleNode[] newChild = new FlexibleNode[numChildren - 1];
int j = 0;
boolean found = false;
for (int i = 0; i < numChildren; i++) {
if (child[i] != n) {
newChild[j] = child[i];
j++;
} else
found = true;
}
if (!found)
throw new IllegalArgumentException("Nonexistent child");
//remove parent link from removed child!
n.setParent(null);
child = newChild;
return n;
}
/**
* remove child
*
* @param n number of child to be removed
*/
public FlexibleNode removeChild(int n) {
int numChildren = getChildCount();
if (n >= numChildren) {
throw new IllegalArgumentException("Nonexistent child");
}
return removeChild(child[n]);
}
/**
* check whether this node has any children
*
* @return result (true or false)
*/
public boolean hasChildren() {
return (getChildCount() != 0);
}
/**
* check whether this node is an external node
*
* @return result (true or false)
*/
public boolean isExternal() {
return !hasChildren();
}
/**
* check whether this node is a root node
*
* @return result (true or false)
*/
public boolean isRoot() {
return (getParent() == null);
}
/**
* Returns the number of children this node has.
*/
public final int getChildCount() {
if (child == null) return 0;
return child.length;
}
// **************************************************************
// Identifiable IMPLEMENTATION
// **************************************************************
private String id = null;
/**
* @return the id as a string.
*/
public String getId() {
return id;
}
/**
* set the id as a string.
*/
public void setId(String id) {
this.id = id;
}
// **************************************************************
// Attributable IMPLEMENTATION
// **************************************************************
private Attributable.AttributeHelper attributes = null;
/**
* Sets an named attribute for this object.
*
* @param name the name of the attribute.
* @param value the new value of the attribute.
*/
public void setAttribute(String name, Object value) {
if (attributes == null)
attributes = new Attributable.AttributeHelper();
attributes.setAttribute(name, value);
}
/**
* @param name the name of the attribute of interest.
* @return an object representing the named attributed for this object.
*/
public Object getAttribute(String name) {
if (attributes == null) {
return null;
} else {
return attributes.getAttribute(name);
}
}
/**
* @return an iterator of the attributes that this object has.
*/
public Iterator<String> getAttributeNames() {
if (attributes == null)
return null;
else
return attributes.getAttributeNames();
}
}