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