package org.iplantc.phyloviewer.viewer.client.model; import java.util.Set; import org.iplantc.phyloviewer.shared.model.INode; import org.iplantc.phyloviewer.shared.model.Node; import com.google.gwt.user.client.rpc.IsSerializable; public class RemoteNode extends Node implements IsSerializable { private int numChildren; private int numNodes; private int numLeaves; private int height; private int depth; /** any node (in the same tree) with a leftIndex >= this.leftIndex and rightIndex <= this.rightIndex is in this node's subtree */ private int leftIndex; private int rightIndex; /** * Creates a node without children. Children can be added with setChildren(), or be fetched (on the * client), using getChildrenAsync() */ public RemoteNode(int id, String label, int numChildren, int numNodes, int numLeaves, int depth, int height, int leftIndex, int rightIndex) { super(id, label); //TODO do some validation on these this.numChildren = numChildren; this.numNodes = numNodes; this.numLeaves = numLeaves; this.depth = depth; this.height = height; this.leftIndex = leftIndex; this.rightIndex = rightIndex; } /** no-arg constructor required for serialization */ public RemoteNode() { } @Override public int getNumberOfLeafNodes() { return numLeaves; } @Override public String findLabelOfFirstLeafNode() { if(numChildren==0) { return getLabel(); } return this.getChild(0).findLabelOfFirstLeafNode(); } @Override public int findMaximumDepthToLeaf() { return height; } /** * @return the number of children this node has. These children may not have been fetched yet. */ @Override public int getNumberOfChildren() { return numChildren; } @Override public int getNumberOfNodes() { return numNodes; } public int getNumberOfLocalNodes() { int count = 1; if (getChildren() != null) { for (INode child : getChildren()) { if (child instanceof RemoteNode) { count += ((RemoteNode)child).getNumberOfLocalNodes(); } else { count += child.getNumberOfNodes(); } } } return count; } @Override public boolean equals(Object obj) { if(obj == null || !(obj instanceof RemoteNode)) { return false; } RemoteNode that = (RemoteNode)obj; return super.shallowEquals(that) && this.numChildren == that.getNumberOfChildren() && this.numLeaves == that.getNumberOfLeafNodes() && this.height == that.findMaximumDepthToLeaf() && this.numNodes == that.getNumberOfNodes(); } @Override public int hashCode() { return getId(); } @Override public void setChildren(Node[] children) { /* * TODO Adding non-RemoteNode children invalidates the topology fields (right/left indices, height, etc). Make sure children * are RemoteNodes with the correct topology fields */ super.setChildren(children); if (children != null) { this.numChildren = children.length; } } public boolean subtreeContains(int traversalIndex) { return traversalIndex >= leftIndex && traversalIndex <= rightIndex; } public boolean subtreeContains(RemoteNode node) { return subtreeContains(node.getLeftIndex()); } /** any node (in the same tree) with a leftIndex >= this.leftIndex and rightIndex <= this.rightIndex is in this node's subtree */ public int getLeftIndex() { return leftIndex; } /** any node (in the same tree) with a leftIndex >= this.leftIndex and rightIndex <= this.rightIndex is in this node's subtree */ public int getRightIndex() { return rightIndex; } public int getDepth() { return depth; } @Override public RemoteNode getChild(int index) { return (RemoteNode) super.getChild(index); } @Override public RemoteNode[] getChildren() { if(super.getChildren() == null) { return null; } int numChildren = this.getNumberOfChildren(); RemoteNode[] array = new RemoteNode[numChildren]; for(int i = 0; i < numChildren; ++i) { array[i] = this.getChild(i); } return array; } @Override public RemoteNode mrca(Set<INode> nodes) { if (nodes == null || nodes.isEmpty()) { return null; } int minLeft = Integer.MAX_VALUE; int maxRight = Integer.MIN_VALUE; for (INode node : nodes) { RemoteNode rNode = (RemoteNode)node; minLeft = Math.min(minLeft, rNode.getLeftIndex()); maxRight = Math.max(maxRight, rNode.getRightIndex()); } RemoteNode mrca = localMRCA(minLeft, maxRight); return mrca; } public RemoteNode localMRCA(int minLeft, int maxRight) { RemoteNode mrca = null; if (this.leftIndex <= minLeft && this.rightIndex >= maxRight) { /* * For localMRCA(), assuming MRCA is this node if the children haven't been fetched, so that * the method doesn't have to go async. Obviously this is not generally true. TODO write a * real async MRCA function that checks on the server. */ mrca = this; for (RemoteNode child : this.getChildren()) { RemoteNode childMRCA = child.localMRCA(minLeft, maxRight); if (childMRCA != null) { mrca = childMRCA; } } } return mrca; } }