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