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