JAL-1551 formatting
[jalview.git] / src / jalview / gui / RedundancyPanel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import jalview.analysis.AlignSeq;
24 import jalview.commands.CommandI;
25 import jalview.commands.EditCommand;
26 import jalview.commands.EditCommand.Action;
27 import jalview.datamodel.SequenceGroup;
28 import jalview.datamodel.SequenceI;
29 import jalview.jbgui.GSliderPanel;
30 import jalview.util.MessageManager;
31
32 import java.awt.event.ActionEvent;
33 import java.util.ArrayList;
34 import java.util.HashMap;
35 import java.util.List;
36 import java.util.Stack;
37 import java.util.Vector;
38
39 import javax.swing.JInternalFrame;
40 import javax.swing.JProgressBar;
41 import javax.swing.event.ChangeEvent;
42 import javax.swing.event.ChangeListener;
43 import javax.swing.event.InternalFrameAdapter;
44 import javax.swing.event.InternalFrameEvent;
45
46 /**
47  * DOCUMENT ME!
48  * 
49  * @author $author$
50  * @version $Revision$
51  */
52 public class RedundancyPanel extends GSliderPanel implements Runnable
53 {
54   AlignFrame af;
55
56   AlignmentPanel ap;
57
58   Stack<CommandI> historyList = new Stack<CommandI>();
59
60   // simpler than synching with alignFrame.
61
62   float[] redundancy;
63
64   SequenceI[] originalSequences;
65
66   JInternalFrame frame;
67
68   Vector redundantSeqs;
69
70   private SequenceI[] redreps;
71
72   /**
73    * Creates a new RedundancyPanel object.
74    * 
75    * @param ap
76    *          DOCUMENT ME!
77    * @param af
78    *          DOCUMENT ME!
79    */
80   public RedundancyPanel(final AlignmentPanel ap, AlignFrame af)
81   {
82     this.ap = ap;
83     this.af = af;
84     redundantSeqs = new Vector();
85
86     slider.addChangeListener(new ChangeListener()
87     {
88       @Override
89       public void stateChanged(ChangeEvent evt)
90       {
91         valueField.setText(slider.getValue() + "");
92         sliderValueChanged();
93       }
94     });
95
96     applyButton.setText(MessageManager.getString("action.hide"));
97     allGroupsCheck.setVisible(false);
98     slider.setMinimum(0);
99     slider.setMaximum(100);
100     slider.setValue(100);
101
102     Thread worker = new Thread(this);
103     worker.start();
104
105     frame = new JInternalFrame();
106     frame.setContentPane(this);
107     Desktop.addInternalFrame(frame, MessageManager
108             .getString("label.redundancy_threshold_selection"), 400, 100,
109             false);
110     frame.addInternalFrameListener(new InternalFrameAdapter()
111     {
112       @Override
113       public void internalFrameClosing(InternalFrameEvent evt)
114       {
115         ap.getIdPanel().getIdCanvas().setHighlighted(null);
116       }
117     });
118
119   }
120
121   /**
122    * This is a copy of remove redundancy in jalivew.datamodel.Alignment except
123    * we dont want to remove redundancy, just calculate once so we can use the
124    * slider to dynamically hide redundant sequences
125    * 
126    * @param threshold
127    *          DOCUMENT ME!
128    * @param sel
129    *          DOCUMENT ME!
130    * 
131    * @return DOCUMENT ME!
132    */
133   @Override
134   public void run()
135   {
136     JProgressBar progress = new JProgressBar();
137     progress.setIndeterminate(true);
138     southPanel.add(progress, java.awt.BorderLayout.SOUTH);
139
140     label.setText(MessageManager.getString("label.calculating"));
141
142     slider.setVisible(false);
143     applyButton.setEnabled(false);
144     valueField.setVisible(false);
145
146     validate();
147
148     String[] omitHidden = null;
149
150     SequenceGroup sg = ap.av.getSelectionGroup();
151     int height;
152
153     int start, end;
154
155     if ((sg != null) && (sg.getSize() >= 1))
156     {
157       originalSequences = sg.getSequencesInOrder(ap.av.getAlignment());
158       start = sg.getStartRes();
159       end = sg.getEndRes();
160     }
161     else
162     {
163       originalSequences = ap.av.getAlignment().getSequencesArray();
164       start = 0;
165       end = ap.av.getAlignment().getWidth();
166     }
167
168     height = originalSequences.length;
169     if (ap.av.hasHiddenColumns())
170     {
171       omitHidden = ap.av.getViewAsString(sg != null);
172     }
173     Object rr[] = AlignSeq.computeRedundancyMatrixWithRep(
174             originalSequences,
175             omitHidden, start, end, false);
176
177     redundancy = (float[]) rr[0];
178     redreps = (SequenceI[]) rr[1];
179
180     progress.setIndeterminate(false);
181     progress.setVisible(false);
182     progress = null;
183
184     label.setText(MessageManager
185             .getString("label.enter_redundancy_thereshold"));
186     slider.setVisible(true);
187     applyButton.setEnabled(true);
188     valueField.setVisible(true);
189
190     validate();
191     sliderValueChanged();
192     // System.out.println((System.currentTimeMillis()-start));
193   }
194
195   void sliderValueChanged()
196   {
197     if (redundancy == null)
198     {
199       return;
200     }
201
202     float value = slider.getValue();
203     List<SequenceI> redundantSequences = new ArrayList<SequenceI>();
204     for (int i = 0; i < redundancy.length; i++)
205     {
206       if (value <= redundancy[i])
207       {
208         redundantSequences.add(originalSequences[i]);
209       }
210     }
211     ap.getIdPanel().getIdCanvas().setHighlighted(redundantSequences);
212   }
213
214   /**
215    * DOCUMENT ME!
216    * 
217    * @param e
218    *          DOCUMENT ME!
219    */
220   @Override
221   public void applyButton_actionPerformed(ActionEvent e)
222   {
223
224     undoButton.setEnabled(true);
225
226     float value = slider.getValue();
227     SequenceGroup sg = ap.av.getSelectionGroup();
228     // delete_seqs(value, sg);
229     hide_seqs(value, sg);
230   }
231   private void hide_seqs(float value, SequenceGroup sg)
232   {
233     /**
234      * hash to look up the representative for a sequence
235      */
236     HashMap<SequenceI, SequenceI> rep = new HashMap<SequenceI, SequenceI>();
237     /**
238      * hash to collect lists of sequences represented by a sequence
239      */
240     HashMap<SequenceI, SequenceGroup> reps = new HashMap<SequenceI, SequenceGroup>();
241     for (int i = 0; i < redundancy.length; i++)
242     {
243       if (value <= redundancy[i])
244       {
245         // does this sequence represent other sequences ?
246         SequenceGroup repset;
247         // is the representative also redundant ?
248         SequenceI repForI = rep.get(redreps[i]);
249         if (repForI==null) {
250           // the representative is still in the alignment. 
251           // is it representing anything already ? 
252           repset = reps.get(redreps[i]);
253           if (repset==null)
254           {
255             repset = new SequenceGroup();
256           }
257           repset.addSequence(originalSequences[i], false);
258           rep.put(originalSequences[i], redreps[i]);
259           reps.put(redreps[i], repset);
260           // and save the representative sequence for originalSeq
261           repForI = redreps[i];
262         } else {
263           // already hidden the representative for this sequence, so look up its redundant peers
264           repset = reps.get(repForI);
265           if (repset==null)
266           {
267             throw new Error("Implementation failure for redundancy set creation");
268           }
269           // add the sequence to the peerset, and mark sequence's representative in hash
270           repset.addSequence(originalSequences[i], false);
271           rep.put(originalSequences[i], repForI);
272         }
273         // merge any sequences represented by this with its new containing group
274         SequenceGroup existingreps = reps.remove(originalSequences[i]);
275         if (existingreps!=null)
276         {
277           for (SequenceI sq:existingreps.getSequences())
278           {
279             rep.put(sq, repForI);
280             repset.addSequence(sq, false);
281           }
282         }
283       }
284     }
285     for (SequenceI repseq: reps.keySet())
286     {
287       sg = reps.get(repseq);
288       sg.addSequence(repseq, false);
289       sg.setSeqrep(repseq);
290       ap.av.hideRepSequences(repseq, sg);
291     }
292   }
293
294   private void delete_seqs(float value, SequenceGroup sg)
295   {
296     ArrayList<SequenceI> del = new ArrayList<SequenceI>();
297
298     for (int i = 0; i < redundancy.length; i++)
299     {
300       if (value <= redundancy[i])
301       {
302         del.add(originalSequences[i]);
303       }
304     }
305     // This has to be done before the restoreHistoryItem method of alignFrame
306     // will
307     // actually restore these sequences.
308     if (del.size() > 0)
309     {
310       SequenceI[] deleted = new SequenceI[del.size()];
311
312       int width = 0;
313       for (int i = 0; i < del.size(); i++)
314       {
315         deleted[i] = del.get(i);
316         if (deleted[i].getLength() > width)
317         {
318           width = deleted[i].getLength();
319         }
320       }
321
322       EditCommand cut = new EditCommand(
323               MessageManager.getString("action.remove_redundancy"),
324               Action.CUT, deleted, 0, width, ap.av.getAlignment());
325
326       for (int i = 0; i < del.size(); i++)
327       {
328         ap.av.getAlignment().deleteSequence(deleted[i]);
329         if (sg != null)
330         {
331           sg.deleteSequence(deleted[i], false);
332         }
333       }
334
335       historyList.push(cut);
336
337       ap.alignFrame.addHistoryItem(cut);
338
339       PaintRefresher.Refresh(this, ap.av.getSequenceSetId(), true, true);
340       ap.av.firePropertyChange("alignment", null, ap.av.getAlignment()
341               .getSequences());
342     }
343
344   }
345
346   /**
347    * DOCUMENT ME!
348    * 
349    * @param e
350    *          DOCUMENT ME!
351    */
352   @Override
353   public void undoButton_actionPerformed(ActionEvent e)
354   {
355     if (historyList == null || historyList.isEmpty())
356     {
357       undoButton.setEnabled(false);
358       return;
359     }
360
361     CommandI command = historyList.pop();
362     if (ap.av.getHistoryList().contains(command))
363     {
364       command.undoCommand(af.getViewAlignments());
365       ap.av.getHistoryList().remove(command);
366       ap.av.firePropertyChange("alignment", null, ap.av.getAlignment()
367               .getSequences());
368       af.updateEditMenuBar();
369     }
370
371     ap.paintAlignment(true);
372
373     if (historyList.size() == 0)
374     {
375       undoButton.setEnabled(false);
376     }
377   }
378
379   /**
380    * DOCUMENT ME!
381    * 
382    * @param e
383    *          DOCUMENT ME!
384    */
385   @Override
386   public void valueField_actionPerformed(ActionEvent e)
387   {
388     try
389     {
390       int i = Integer.parseInt(valueField.getText());
391       slider.setValue(i);
392     } catch (Exception ex)
393     {
394       valueField.setText(slider.getValue() + "");
395     }
396   }
397
398 }