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