package jalview.ext.archaeopteryx; import jalview.datamodel.SequenceI; import jalview.ext.forester.DataConversions; import jalview.ext.treeviewer.TreeNodeI; import java.awt.Color; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.forester.phylogeny.PhylogenyMethods; import org.forester.phylogeny.PhylogenyNode; public class TreeNode implements TreeNodeI { private final PhylogenyNode node; private SequenceI nodeSeq; private static Map originalNodes = new HashMap<>( 500); // prolly make this size dynamic? private static Map wrappedNodes = new HashMap<>( 500); /** * Please don't use me directly. * * @param aptxNode */ private TreeNode(PhylogenyNode aptxNode) { node = aptxNode; if (aptxNode.getNodeData().getSequence() != null) { nodeSeq = DataConversions .createJalviewSequence(aptxNode); } originalNodes.put(aptxNode, this); wrappedNodes.put(this, aptxNode); } @Override public String getNodeName() { return node.getName(); } @Override public List getAllDescendants() { List descNodes = PhylogenyMethods .getAllDescendants(node); return getUniqueWrappers(descNodes); } @Override public List getExternalDescendants() { List extDescNodes = node.getAllExternalDescendants(); return getUniqueWrappers(extDescNodes); } @Override public List getDirectChildren() { List childNodes = node.getDescendants(); return getUniqueWrappers(childNodes); } @Override public void setSequence(SequenceI seq) { nodeSeq = seq; org.forester.phylogeny.data.Sequence foresterFormatSeq = DataConversions .createForesterSequence(seq, true); node.getNodeData().setSequence(foresterFormatSeq); } @Override public SequenceI getSequence() { return nodeSeq; } @Override public void addAsChild(TreeNodeI childNode) { PhylogenyNode aptxNode = unwrapNode(childNode); node.addAsChild(aptxNode); } @Override public long getId() { return node.getId(); } @Override public float getXcoord() { return node.getXcoord(); } @Override public void setBranchColor(Color branchColor) { PhylogenyMethods.setBranchColorValue(node, branchColor); } @Override public boolean isInternal() { return node.isInternal(); } public static List getUniqueWrappers( List aptxNodes) { List wrappedNodes = new ArrayList<>( aptxNodes.size()); for (PhylogenyNode aptxNode : aptxNodes) { wrappedNodes.add(getUniqueWrapper(aptxNode)); } return wrappedNodes; } /** * This method should be used to create new wrappers as there is a possibility * the Archaeopteryx node was already introduced to Jalview previously so this * avoids giving one node duplicate wrappers * * @param aptxNode * @return */ public static TreeNodeI getUniqueWrapper( PhylogenyNode aptxNode) { if (aptxNode == null) { return null; } TreeNodeI wrappedNode = originalNodes.get(aptxNode); if (wrappedNode == null) { wrappedNode = new TreeNode(aptxNode); } return wrappedNode; } /** * Attempts to unwrap the given node, if the unwrapped node already exists it * is simply returned as is. If it is not however, the wrapper will be used to * create a new Archaeopteryx node. This way it becomes possible to construct * new Archaeopteryx nodes from different tree viewers, as long as they * implement the interface. * * @param wrappedNode * @return */ protected static PhylogenyNode unwrapNode(TreeNodeI wrappedNode) { if (wrappedNode == null) { return null; } PhylogenyNode aptxNode = wrappedNodes.get(wrappedNode); if (aptxNode == null) { // expand this aptxNode = new PhylogenyNode(wrappedNode.getNodeName()); } return aptxNode; } @Override public int hashCode() { final int prime = 31; int result = 1; result = (int) (prime * result + ((node == null) ? 0 : (node.hashCode() * getId()))); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } TreeNode other = (TreeNode) obj; if (node == null) { if (other.node != null) { return false; } } if (getId() != other.getId()) { return false; } if (!node.equals(other.node)) { return false; } return true; } @Override public float getYcoord() { return node.getYcoord(); } @Override public boolean isCollapsed() { return node.isCollapse(); } }