JAL-1482 JAL-653 refactor feature file import code to alignview controller
[jalview.git] / src / jalview / controller / AlignViewController.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.controller;
22
23 import jalview.analysis.AlignmentSorter;
24 import jalview.api.AlignViewControllerGuiI;
25 import jalview.api.AlignViewControllerI;
26 import jalview.api.AlignViewportI;
27 import jalview.api.AlignmentViewPanel;
28 import jalview.api.FeatureRenderer;
29 import jalview.commands.OrderCommand;
30 import jalview.datamodel.AlignmentI;
31 import jalview.datamodel.ColumnSelection;
32 import jalview.datamodel.SequenceCollectionI;
33 import jalview.datamodel.SequenceFeature;
34 import jalview.datamodel.SequenceGroup;
35 import jalview.datamodel.SequenceI;
36 import jalview.io.FeaturesFile;
37 import jalview.util.MessageManager;
38
39 import java.awt.Color;
40 import java.util.ArrayList;
41 import java.util.BitSet;
42 import java.util.List;
43
44 public class AlignViewController implements AlignViewControllerI
45 {
46   AlignViewportI viewport = null;
47
48   AlignmentViewPanel alignPanel = null;
49
50   /**
51    * the GUI container that is handling interactions with the user
52    */
53   private AlignViewControllerGuiI avcg;
54
55   @Override
56   protected void finalize() throws Throwable
57   {
58     viewport = null;
59     alignPanel = null;
60     avcg = null;
61   };
62
63   public AlignViewController(AlignViewControllerGuiI alignFrame,
64           AlignViewportI viewport, AlignmentViewPanel alignPanel)
65   {
66     this.avcg = alignFrame;
67     this.viewport = viewport;
68     this.alignPanel = alignPanel;
69   }
70
71   @Override
72   public void setViewportAndAlignmentPanel(AlignViewportI viewport,
73           AlignmentViewPanel alignPanel)
74   {
75     this.alignPanel = alignPanel;
76     this.viewport = viewport;
77
78   }
79
80   @Override
81   public boolean makeGroupsFromSelection()
82   {
83     SequenceGroup sg = viewport.getSelectionGroup();
84     ColumnSelection cs = viewport.getColumnSelection();
85     SequenceGroup[] gps = null;
86     if (sg != null
87             && (cs == null || cs.getSelected() == null || cs.size() == 0))
88     {
89       gps = jalview.analysis.Grouping.makeGroupsFrom(
90               viewport.getSequenceSelection(),
91               viewport.getAlignmentView(true).getSequenceStrings(
92                       viewport.getGapCharacter()), viewport.getAlignment()
93                       .getGroups());
94     } else {
95       if (cs!=null) {
96         gps = jalview.analysis.Grouping.makeGroupsFromCols(
97                 (sg == null) ? viewport.getAlignment().getSequencesArray()
98                         : sg.getSequences().toArray(new SequenceI[0]), cs,
99                 viewport.getAlignment().getGroups());
100       }
101     }
102     if (gps!=null) {
103       viewport.getAlignment().deleteAllGroups();
104       viewport.clearSequenceColours();
105       viewport.setSelectionGroup(null);
106       // set view properties for each group
107       for (int g = 0; g < gps.length; g++)
108       {
109         // gps[g].setShowunconserved(viewport.getShowUnconserved());
110         gps[g].setshowSequenceLogo(viewport.isShowSequenceLogo());
111         viewport.getAlignment().addGroup(gps[g]);
112         Color col = new Color((int) (Math.random() * 255),
113                 (int) (Math.random() * 255), (int) (Math.random() * 255));
114         col = col.brighter();
115         for (SequenceI sq : gps[g].getSequences(null))
116         {
117           viewport.setSequenceColour(sq, col);
118         }
119       }
120       return true;
121     }
122     return false;
123   }
124
125   @Override
126   public boolean createGroup()
127   {
128
129     SequenceGroup sg = viewport.getSelectionGroup();
130     if (sg != null)
131     {
132       viewport.getAlignment().addGroup(sg);
133       return true;
134     }
135     return false;
136   }
137
138   @Override
139   public boolean unGroup()
140   {
141     SequenceGroup sg = viewport.getSelectionGroup();
142     if (sg != null)
143     {
144       viewport.getAlignment().deleteGroup(sg);
145       return true;
146     }
147     return false;
148   }
149
150   @Override
151   public boolean deleteGroups()
152   {
153     if (viewport.getAlignment().getGroups() != null
154             && viewport.getAlignment().getGroups().size() > 0)
155     {
156       viewport.getAlignment().deleteAllGroups();
157       viewport.clearSequenceColours();
158       viewport.setSelectionGroup(null);
159       return true;
160     }
161     return false;
162   }
163
164   @Override
165   public boolean markColumnsContainingFeatures(boolean invert,
166           boolean extendCurrent, boolean toggle, String featureType)
167   {
168     // JBPNote this routine could also mark rows, not just columns.
169     // need a decent query structure to allow all types of feature searches
170     BitSet bs = new BitSet();
171     int alw, alStart;
172     SequenceCollectionI sqcol = (viewport.getSelectionGroup() == null ? viewport
173             .getAlignment() : viewport.getSelectionGroup());
174     alStart = sqcol.getStartRes();
175     alw = sqcol.getEndRes() + 1;
176     List<SequenceI> seqs = sqcol.getSequences();
177     int nseq = 0;
178     for (SequenceI sq : seqs)
179     {
180       int tfeat = 0;
181       if (sq != null)
182       {
183         SequenceFeature[] sf = sq.getSequenceFeatures();
184         if (sf != null)
185         {
186           int ist = sq.findIndex(sq.getStart());
187           int iend = sq.findIndex(sq.getEnd());
188           if (iend < alStart || ist > alw)
189           {
190             // sequence not in region
191             continue;
192           }
193           for (SequenceFeature sfpos : sf)
194           {
195             // future functionalty - featureType == null means mark columns
196             // containing all displayed features
197             if (sfpos != null && (featureType.equals(sfpos.getType())))
198             {
199               tfeat++;
200               // optimisation - could consider 'spos,apos' like cursor argument
201               // - findIndex wastes time by starting from first character and
202               // counting
203
204               int i = sq.findIndex(sfpos.getBegin());
205               int j = sq.findIndex(sfpos.getEnd());
206               if (j < alStart || i > alw)
207               {
208                 // feature is outside selected region
209                 continue;
210               }
211               if (i < alStart)
212               {
213                 i = alStart;
214               }
215               if (i < ist)
216               {
217                 i = ist;
218               }
219               if (j > alw)
220               {
221                 j = alw;
222               }
223               for (; i <= j; i++)
224               {
225                 bs.set(i - 1);
226               }
227             }
228           }
229         }
230
231         if (tfeat > 0)
232         {
233           nseq++;
234         }
235       }
236     }
237     ColumnSelection cs = viewport.getColumnSelection();
238     if (bs.cardinality() > 0 || invert)
239     {
240       if (cs == null)
241       {
242         cs = new ColumnSelection();
243       }
244       else
245       {
246         if (!extendCurrent)
247         {
248           cs.clear();
249         }
250       }
251       if (invert)
252       {
253         // invert only in the currently selected sequence region
254         for (int i = bs.nextClearBit(alStart), ibs = bs.nextSetBit(alStart); i >= alStart
255                 && i < (alw);)
256         {
257           if (ibs < 0 || i < ibs)
258           {
259             if (toggle && cs.contains(i))
260             {
261               cs.removeElement(i++);
262             }
263             else
264             {
265               cs.addElement(i++);
266             }
267           }
268           else
269           {
270             i = bs.nextClearBit(ibs);
271             ibs = bs.nextSetBit(i);
272           }
273         }
274       }
275       else
276       {
277         for (int i = bs.nextSetBit(alStart); i >= alStart; i = bs
278                 .nextSetBit(i + 1))
279         {
280           if (toggle && cs.contains(i))
281           {
282             cs.removeElement(i);
283           }
284           else
285           {
286             cs.addElement(i);
287           }
288         }
289       }
290       viewport.setColumnSelection(cs);
291       alignPanel.paintAlignment(true);
292       avcg.setStatus(MessageManager.formatMessage("label.view_controller_toggled_marked",
293                   new String[]{
294                                 (toggle ? MessageManager.getString("label.toggled") : MessageManager.getString("label.marked")),
295                                 (invert ? (Integer.valueOf((alw - alStart) - bs.cardinality()).toString()):(Integer.valueOf(bs.cardinality()).toString())),
296                                 featureType, Integer.valueOf(nseq).toString()
297                         }));
298       return true;
299     }
300     else
301     {
302       avcg.setStatus(MessageManager.formatMessage("label.no_feature_of_type_found", new String[]{featureType}));
303       if (!extendCurrent && cs != null)
304       {
305         cs.clear();
306         alignPanel.paintAlignment(true);
307       }
308       return false;
309     }
310   }
311
312
313
314   @Override
315   public void sortAlignmentByFeatureDensity(String[] typ)
316   {
317     sortBy(typ, "Sort by Density", AlignmentSorter.FEATURE_DENSITY);
318   }
319
320   protected void sortBy(String[] typ, String methodText, final String method)
321   {
322     FeatureRenderer fr = alignPanel.getFeatureRenderer();
323     if (typ == null)
324     {
325       typ = fr==null ? null : fr.getDisplayedFeatureTypes();
326     }
327     String gps[] = null;
328     gps = fr==null ? null : fr.getDisplayedFeatureGroups();
329     if (typ != null)
330     {
331       ArrayList types = new ArrayList();
332       for (int i = 0; i < typ.length; i++)
333       {
334         if (typ[i] != null)
335         {
336           types.add(typ[i]);
337         }
338         typ = new String[types.size()];
339         types.toArray(typ);
340       }
341     }
342     if (gps != null)
343     {
344       ArrayList grps = new ArrayList();
345
346       for (int i = 0; i < gps.length; i++)
347       {
348         if (gps[i] != null)
349         {
350           grps.add(gps[i]);
351         }
352       }
353       gps = new String[grps.size()];
354       grps.toArray(gps);
355     }
356     AlignmentI al = viewport.getAlignment();
357
358     int start, stop;
359     SequenceGroup sg = viewport.getSelectionGroup();
360     if (sg != null)
361     {
362       start = sg.getStartRes();
363       stop = sg.getEndRes();
364     }
365     else
366     {
367       start = 0;
368       stop = al.getWidth();
369     }
370     SequenceI[] oldOrder = al.getSequencesArray();
371     AlignmentSorter.sortByFeature(typ, gps, start, stop, al, method);
372     avcg.addHistoryItem(new OrderCommand(methodText, oldOrder, viewport
373             .getAlignment()));
374     alignPanel.paintAlignment(true);
375
376   }
377
378   @Override
379   public void sortAlignmentByFeatureScore(String[] typ)
380   {
381     sortBy(typ, "Sort by Feature Score", AlignmentSorter.FEATURE_SCORE);
382   }
383
384   @Override
385   public boolean parseFeaturesFile(String file, String protocol,
386           boolean relaxedIdMatching)
387   {
388     boolean featuresFile = false;
389     try
390     {
391       featuresFile = new FeaturesFile(file, protocol).parse(viewport
392               .getAlignment().getDataset(), alignPanel.getFeatureRenderer()
393               .getFeatureColours(), false, relaxedIdMatching);
394     } catch (Exception ex)
395     {
396       ex.printStackTrace();
397     }
398
399     if (featuresFile)
400     {
401       avcg.refreshFeatureUI(true);
402       if (alignPanel.getFeatureRenderer() != null)
403       {
404         // update the min/max ranges where necessary
405         alignPanel.getFeatureRenderer().findAllFeatures(true);
406       }
407       if (avcg.getFeatureSettingsUI() != null)
408       {
409         avcg.getFeatureSettingsUI().discoverAllFeatureData();
410       }
411       alignPanel.paintAlignment(true);
412     }
413
414     return featuresFile;
415
416   }
417 }