JAL-1953 more progress on interfaces, jalview binding still problematic
[jalview.git] / src / jalview / ext / archaeopteryx / TreeNode.java
1 package jalview.ext.archaeopteryx;
2
3 import jalview.datamodel.SequenceI;
4 import jalview.ext.treeviewer.ExternalTreeNodeI;
5
6 import java.awt.Color;
7 import java.util.ArrayList;
8 import java.util.HashMap;
9 import java.util.List;
10 import java.util.Map;
11
12 import org.forester.phylogeny.PhylogenyMethods;
13 import org.forester.phylogeny.PhylogenyNode;
14 import org.forester.phylogeny.data.BranchColor;
15
16 public class TreeNode implements ExternalTreeNodeI
17 {
18   private final PhylogenyNode node;
19
20   private SequenceI nodeSeq;
21
22   private static Map<PhylogenyNode, ExternalTreeNodeI> originalNodes = new HashMap<>(
23           500); // prolly make this size dynamic
24
25   private static Map<ExternalTreeNodeI, PhylogenyNode> wrappedNodes = new HashMap<>(
26           500);
27
28   private TreeNode(PhylogenyNode aptxNode)
29   {
30     node = aptxNode;
31     originalNodes.put(aptxNode, this);
32     wrappedNodes.put(this, aptxNode);
33
34   }
35
36
37   @Override
38   public String getNodeName()
39   {
40     return node.getName();
41   }
42
43
44   @Override
45   public List<ExternalTreeNodeI> getAllDescendants()
46   {
47
48     List<PhylogenyNode> descNodes = PhylogenyMethods
49             .getAllDescendants(node);
50     return getUniqueWrappers(descNodes);
51     
52
53   }
54
55   @Override
56   public List<ExternalTreeNodeI> getExternalDescendants()
57   {
58     List<PhylogenyNode> extDescNodes = node.getAllExternalDescendants();
59     return getUniqueWrappers(extDescNodes);
60   }
61
62
63   @Override
64   public List<ExternalTreeNodeI> getDirectChildren()
65   {
66     List<PhylogenyNode> childNodes = node.getDescendants();
67     return getUniqueWrappers(childNodes);
68     
69
70   }
71
72
73
74   @Override
75   public void setSequence(SequenceI seq)
76   {
77     nodeSeq = seq;
78     org.forester.phylogeny.data.Sequence foresterFormatSeq = DataConversions
79             .createForesterSequence(seq, true);
80     node.getNodeData().setSequence(foresterFormatSeq);
81
82   }
83
84   @Override
85   public SequenceI getSequence()
86   {
87     return nodeSeq;
88     // ideally this would return a converted node.getNodeData().getSequence()
89   }
90
91   @Override
92   public void addAsChild(ExternalTreeNodeI childNode)
93   {
94     PhylogenyNode aptxNode = unwrapNode(childNode);
95
96     node.addAsChild(aptxNode);
97
98   }
99
100   @Override
101   public long getId()
102   {
103     return node.getId();
104   }
105
106   @Override
107   public float getXcoord()
108   {
109     return node.getXcoord();
110   }
111
112   @Override
113   public void setBranchColor(Color branchColor)
114   {
115     node.getBranchData().setBranchColor(new BranchColor(branchColor));
116
117   }
118
119   @Override
120   public boolean isInternal()
121   {
122     return node.isInternal();
123   }
124
125   public static List<ExternalTreeNodeI> getUniqueWrappers(
126           List<PhylogenyNode> aptxNodes)
127   {
128     List<ExternalTreeNodeI> wrappedNodes = new ArrayList<>(
129             aptxNodes.size());
130
131     for (PhylogenyNode aptxNode : aptxNodes)
132     {
133       wrappedNodes.add(getUniqueWrapper(aptxNode));
134     }
135     return wrappedNodes;
136   }
137
138   /**
139    * This method should be used to create new wrappers as there is a possibility
140    * the Archaeopteryx node was already introduced to Jalview previously so this
141    * avoids giving one node duplicate wrappers
142    * 
143    * @param aptxNode
144    * @return
145    */
146   public static ExternalTreeNodeI getUniqueWrapper(
147           PhylogenyNode aptxNode)
148   {
149     ExternalTreeNodeI wrappedNode = originalNodes.get(aptxNode);
150     if (wrappedNode == null)
151     {
152       wrappedNode = new TreeNode(aptxNode);
153     }
154
155     return wrappedNode;
156   }
157
158   /**
159    * Attempts to unwrap the given node, if the unwrapped node already exists it
160    * is simply returned as is. If it is not however, the wrapper will be used to
161    * create a new Archaeopteryx node. This way it becomes possible to construct
162    * new Archaeopteryx nodes from different tree viewers, as long as they
163    * implement the interface.
164    * 
165    * @param wrappedNode
166    * @return
167    */
168   protected static PhylogenyNode unwrapNode(ExternalTreeNodeI wrappedNode)
169   {
170     PhylogenyNode aptxNode = wrappedNodes.get(wrappedNode);
171     if (aptxNode == null)
172     {
173       // expand this
174       aptxNode = new PhylogenyNode(wrappedNode.getNodeName());
175     }
176     return aptxNode;
177
178   }
179
180
181   @Override
182   public int hashCode()
183   {
184     final int prime = 31;
185     int result = 1;
186     result = prime * result + ((node == null) ? 0 : node.hashCode());
187     return result;
188   }
189
190   @Override
191   public boolean equals(Object obj)
192   {
193     if (this == obj)
194     {
195       return true;
196     }
197     if (obj == null)
198     {
199       return false;
200     }
201     if (getClass() != obj.getClass())
202     {
203       return false;
204     }
205     TreeNode other = (TreeNode) obj;
206     if (node == null)
207     {
208       if (other.node != null)
209       {
210         return false;
211       }
212     }
213     else if (!node.equals(other.node))
214     {
215       return false;
216     }
217     return true;
218   }
219
220
221 }