package org.iplantc.phyloviewer.shared.model;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import com.google.gwt.user.client.rpc.IsSerializable;
public class Node implements INode, IsSerializable
{
private int id;
private String label;
private Vector<Node> children = null;
private Double branchLength;
private transient ArrayList<NodeListener> listeners = new ArrayList<NodeListener>();
public Node(int id, String label)
{
this.id = id;
this.label = label;
}
public Node(Node[] children)
{
this.setChildren(children);
}
public Node()
{
}
@Override
public String findLabelOfFirstLeafNode()
{
if(this.isLeaf())
{
return this.getLabel();
}
return this.getChild(0).findLabelOfFirstLeafNode();
}
@Override
public int findMaximumDepthToLeaf()
{
int maxChildHeight = -1; // -1 so leaf will return 0
for(int index = 0;index < getNumberOfChildren();index++)
{
INode child = getChild(index);
maxChildHeight = Math.max(maxChildHeight, child.findMaximumDepthToLeaf());
}
return maxChildHeight + 1;
}
@Override
public Node getChild(int index)
{
if(children == null)
{
return null;
}
return children.get(index);
}
public void addChild(Node node)
{
if(children == null)
{
children = new Vector<Node>();
}
children.add(node);
}
@Override
public Node[] getChildren()
{
if(children == null)
{
return null;
}
Node[] array = new Node[children.size()];
for(int i = 0;i < children.size();++i)
{
array[i] = children.get(i);
}
return array;
}
@Override
public int getId()
{
return id;
}
@Override
public String getLabel()
{
return label;
}
@Override
public int getNumberOfChildren()
{
if(getChildren() == null)
{
return 0;
}
else
{
return getChildren().length;
}
}
@Override
public int getNumberOfLeafNodes()
{
int count = 0;
if(this.isLeaf())
{
count = 1;
}
else
{
for(int i = 0;i < this.getNumberOfChildren();++i)
{
count += this.getChild(i).getNumberOfLeafNodes();
}
}
return count;
}
@Override
public int getNumberOfNodes()
{
int count = 1;
if(getChildren() != null)
{
for(INode child : getChildren())
{
count += child.getNumberOfNodes();
}
}
return count;
}
@Override
public Boolean isLeaf()
{
return getNumberOfChildren() == 0;
}
@Override
public void setId(int id)
{
this.id = id;
}
@Override
public void setLabel(String label)
{
this.label = label;
}
@Override
public void sortChildrenBy(Comparator<INode> comparator)
{
if(getChildren() != null)
{
List<Node> childList = Arrays.asList(getChildren());
Collections.sort(childList, comparator);
setChildren(childList.toArray(new Node[childList.size()]));
}
}
@Override
public boolean equals(Object obj)
{
if(obj == null || !(obj instanceof Node))
{
return false;
}
Node that = (Node)obj;
return this.shallowEquals(that) && Arrays.equals(this.getChildren(), that.getChildren());
}
public boolean shallowEquals(Node obj)
{
return this.getId() == obj.getId() && this.getLabel().equals(obj.getLabel());
}
public void setChildren(Node[] children)
{
if(children == null)
{
this.children = null;
}
else
{
this.children = new Vector<Node>();
for(Node node : children)
{
this.children.add(node);
}
notifyNodeListeners(children);
}
}
public void addNodeListener(NodeListener listener)
{
listeners.add(listener);
}
public void removeNodeListener(NodeListener listener)
{
listeners.remove(listener);
}
public void removeNodeListenerFromSubtree(NodeListener listener)
{
removeNodeListener(listener);
if(children != null)
{
for(Node child : children)
{
child.removeNodeListenerFromSubtree(listener);
}
}
}
public interface NodeListener
{
/** Called when a Node has set new children */
void handleChildren(Node[] children);
}
private void notifyNodeListeners(Node[] children)
{
for(NodeListener listener : listeners)
{
listener.handleChildren(children);
}
}
@Override
public String toString()
{
return label;
}
@Override
public Double getBranchLength()
{
return branchLength;
}
@Override
public void setBranchLength(Double branchLength)
{
this.branchLength = branchLength;
}
@Override
public double findMaximumDistanceToLeaf()
{
return this.findMaximumDistanceToLeaf(0.0);
}
private double findMaximumDistanceToLeaf(double currentDistance)
{
double localMaximum = currentDistance;
int numChildren = this.getNumberOfChildren();
if(0 < numChildren)
{
for(int i = 0;i < numChildren;++i)
{
Node child = this.getChild(i);
double distance = child.findMaximumDistanceToLeaf(currentDistance);
if(distance > localMaximum)
{
localMaximum = distance;
}
}
}
double branchLength = this.getBranchLength() != null ? this.getBranchLength() : 0.0;
return localMaximum + branchLength;
}
@Override
public String getMetaDataString()
{
return null;
}
@Override
public INode mrca(Set<INode> nodes)
{
/*
* FIXME return the most recent common ancestor of the given set of nodes within this INode's
* subtree. Return null if this subtree does not contain all of the nodes. Probably need a parent
* reference to do this with any kind of efficiency
*/
return null;
}
}