JAL-1953 added Y coordinate getter method
[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     return wrappedNode;
155   }
156
157   /**
158    * Attempts to unwrap the given node, if the unwrapped node already exists it
159    * is simply returned as is. If it is not however, the wrapper will be used to
160    * create a new Archaeopteryx node. This way it becomes possible to construct
161    * new Archaeopteryx nodes from different tree viewers, as long as they
162    * implement the interface.
163    * 
164    * @param wrappedNode
165    * @return
166    */
167   protected static PhylogenyNode unwrapNode(ExternalTreeNodeI wrappedNode)
168   {
169     PhylogenyNode aptxNode = wrappedNodes.get(wrappedNode);
170     if (aptxNode == null)
171     {
172       // expand this
173       aptxNode = new PhylogenyNode(wrappedNode.getNodeName());
174
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 = (int) (prime * result
187             + ((node == null) ? 0 : (node.hashCode() * getId())));
188     return result;
189   }
190
191   @Override
192   public boolean equals(Object obj)
193   {
194     if (this == obj)
195     {
196       return true;
197     }
198     if (obj == null)
199     {
200       return false;
201     }
202     if (getClass() != obj.getClass())
203     {
204       return false;
205     }
206     TreeNode other = (TreeNode) obj;
207     if (node == null)
208     {
209       if (other.node != null)
210       {
211         return false;
212       }
213     }
214     if (getId() != other.getId())
215     {
216       return false;
217     }
218
219     if (!node.equals(other.node))
220     {
221       return false;
222     }
223     return true;
224   }
225
226
227   @Override
228   public float getYcoord()
229   {
230     return node.getYcoord();
231   }
232
233 }