JAL-2154 refactor CrossRef UI code to its own class
[jalview.git] / src / jalview / gui / CrossRefAction.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.AlignmentUtils;
24 import jalview.analysis.CrossRef;
25 import jalview.api.AlignmentViewPanel;
26 import jalview.api.FeatureSettingsModelI;
27 import jalview.bin.Cache;
28 import jalview.datamodel.Alignment;
29 import jalview.datamodel.AlignmentI;
30 import jalview.datamodel.DBRefSource;
31 import jalview.datamodel.SequenceI;
32 import jalview.io.gff.SequenceOntologyI;
33 import jalview.structure.StructureSelectionManager;
34 import jalview.util.MessageManager;
35 import jalview.ws.SequenceFetcher;
36
37 import java.util.ArrayList;
38 import java.util.List;
39
40 /**
41  * Factory constructor and runnable for discovering and displaying
42  * cross-references for a set of aligned sequences
43  * 
44  * @author jprocter
45  *
46  */
47 public class CrossRefAction implements Runnable
48 {
49   private AlignFrame alignFrame;
50
51   private SequenceI[] sel;
52
53   private boolean _odna;
54
55   private String source;
56
57   List<AlignmentViewPanel> xrefViews = new ArrayList<AlignmentViewPanel>();
58
59   public List<jalview.api.AlignmentViewPanel> getXrefViews()
60   {
61     return xrefViews;
62   }
63
64   @Override
65   public void run()
66   {
67     final long sttime = System.currentTimeMillis();
68     alignFrame.setProgressBar(
69             MessageManager.formatMessage(
70                     "status.searching_for_sequences_from",
71                     new Object[] { source }), sttime);
72     try
73     {
74       AlignmentI alignment = alignFrame.getViewport().getAlignment();
75       AlignmentI dataset = alignment.getDataset() == null ? alignment
76               : alignment.getDataset();
77       boolean dna = alignment.isNucleotide();
78       if (_odna != dna)
79       {
80         System.err
81                 .println("Conflict: showProducts for alignment originally "
82                         + "thought to be " + (_odna ? "DNA" : "Protein")
83                         + " now searching for " + (dna ? "DNA" : "Protein")
84                         + " Context.");
85       }
86       AlignmentI xrefs = new CrossRef(sel, dataset).findXrefSequences(
87               source, dna);
88       if (xrefs == null)
89       {
90         return;
91       }
92       /*
93        * get display scheme (if any) to apply to features
94        */
95       FeatureSettingsModelI featureColourScheme = new SequenceFetcher()
96               .getFeatureColourScheme(source);
97
98       AlignmentI xrefsAlignment = makeCrossReferencesAlignment(dataset,
99               xrefs);
100       if (!dna)
101       {
102         xrefsAlignment = AlignmentUtils.makeCdsAlignment(
103                 xrefsAlignment.getSequencesArray(), dataset, sel);
104         xrefsAlignment.alignAs(alignment);
105       }
106
107       /*
108        * If we are opening a splitframe, make a copy of this alignment (sharing the same dataset
109        * sequences). If we are DNA, drop introns and update mappings
110        */
111       AlignmentI copyAlignment = null;
112
113       if (Cache.getDefault(Preferences.ENABLE_SPLIT_FRAME, true))
114       {
115         boolean copyAlignmentIsAligned = false;
116         if (dna)
117         {
118           copyAlignment = AlignmentUtils.makeCdsAlignment(sel, dataset,
119                   xrefsAlignment.getSequencesArray());
120           if (copyAlignment.getHeight() == 0)
121           {
122             System.err.println("Failed to make CDS alignment");
123           }
124
125           /*
126            * pending getting Embl transcripts to 'align', 
127            * we are only doing this for Ensembl
128            */
129           // TODO proper criteria for 'can align as cdna'
130           if (DBRefSource.ENSEMBL.equalsIgnoreCase(source)
131                   || AlignmentUtils.looksLikeEnsembl(alignment))
132           {
133             copyAlignment.alignAs(alignment);
134             copyAlignmentIsAligned = true;
135           }
136         }
137         else
138         {
139           copyAlignment = AlignmentUtils.makeCopyAlignment(sel,
140                   xrefs.getSequencesArray(), dataset);
141         }
142         copyAlignment
143                 .setGapCharacter(alignFrame.viewport.getGapCharacter());
144
145         StructureSelectionManager ssm = StructureSelectionManager
146                 .getStructureSelectionManager(Desktop.instance);
147
148         /*
149          * register any new mappings for sequence mouseover etc
150          * (will not duplicate any previously registered mappings)
151          */
152         ssm.registerMappings(dataset.getCodonFrames());
153
154         if (copyAlignment.getHeight() <= 0)
155         {
156           System.err.println("No Sequences generated for xRef type "
157                   + source);
158           return;
159         }
160         /*
161          * align protein to dna
162          */
163         if (dna && copyAlignmentIsAligned)
164         {
165           xrefsAlignment.alignAs(copyAlignment);
166         }
167         else
168         {
169           /*
170            * align cdna to protein - currently only if 
171            * fetching and aligning Ensembl transcripts!
172            */
173           // TODO: generalise for other sources of locus/transcript/cds data
174           if (dna && DBRefSource.ENSEMBL.equalsIgnoreCase(source))
175           {
176             copyAlignment.alignAs(xrefsAlignment);
177           }
178         }
179       }
180       /*
181        * build AlignFrame(s) according to available alignment data
182        */
183       AlignFrame newFrame = new AlignFrame(xrefsAlignment,
184               AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
185       if (Cache.getDefault("HIDE_INTRONS", true))
186       {
187         newFrame.hideFeatureColumns(SequenceOntologyI.EXON, false);
188       }
189       String newtitle = String.format("%s %s %s", MessageManager
190               .getString(dna ? "label.proteins" : "label.nucleotides"),
191               MessageManager.getString("label.for"), alignFrame.getTitle());
192       newFrame.setTitle(newtitle);
193
194       if (copyAlignment == null)
195       {
196         /*
197          * split frame display is turned off in preferences file
198          */
199         Desktop.addInternalFrame(newFrame, newtitle,
200                 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
201         xrefViews.add(newFrame.alignPanel);
202         return; // via finally clause
203       }
204       AlignFrame copyThis = new AlignFrame(copyAlignment,
205               AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
206       copyThis.setTitle(alignFrame.getTitle());
207
208       boolean showSequenceFeatures = alignFrame.getViewport()
209               .isShowSequenceFeatures();
210       newFrame.setShowSeqFeatures(showSequenceFeatures);
211       copyThis.setShowSeqFeatures(showSequenceFeatures);
212       FeatureRenderer myFeatureStyling = alignFrame.alignPanel
213               .getSeqPanel().seqCanvas.getFeatureRenderer();
214
215       /*
216        * copy feature rendering settings to split frame
217        */
218       newFrame.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer()
219               .transferSettings(myFeatureStyling);
220       copyThis.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer()
221               .transferSettings(myFeatureStyling);
222
223       /*
224        * apply 'database source' feature configuration
225        * if any was found
226        */
227       // TODO is this the feature colouring for the original
228       // alignment or the fetched xrefs? either could be Ensembl
229       newFrame.getViewport().applyFeaturesStyle(featureColourScheme);
230       copyThis.getViewport().applyFeaturesStyle(featureColourScheme);
231
232       SplitFrame sf = new SplitFrame(dna ? copyThis : newFrame,
233               dna ? newFrame : copyThis);
234       newFrame.setVisible(true);
235       copyThis.setVisible(true);
236       String linkedTitle = MessageManager
237               .getString("label.linked_view_title");
238       Desktop.addInternalFrame(sf, linkedTitle, -1, -1);
239       sf.adjustDivider();
240
241       // finally add the top, then bottom frame to the view list
242       xrefViews.add(dna ? copyThis.alignPanel : newFrame.alignPanel);
243       xrefViews.add(!dna ? copyThis.alignPanel : newFrame.alignPanel);
244
245     } catch (OutOfMemoryError e)
246     {
247       new OOMWarning("whilst fetching crossreferences", e);
248     } catch (Throwable e)
249     {
250       Cache.log.error("Error when finding crossreferences", e);
251     } finally
252     {
253       alignFrame.setProgressBar(MessageManager.formatMessage(
254               "status.finished_searching_for_sequences_from",
255               new Object[] { source }), sttime);
256     }
257   }
258
259   /**
260    * Makes an alignment containing the given sequences, and adds them to the
261    * given dataset, which is also set as the dataset for the new alignment
262    * 
263    * TODO: refactor to DatasetI method
264    * 
265    * @param dataset
266    * @param seqs
267    * @return
268    */
269   protected AlignmentI makeCrossReferencesAlignment(AlignmentI dataset,
270           AlignmentI seqs)
271   {
272     SequenceI[] sprods = new SequenceI[seqs.getHeight()];
273     for (int s = 0; s < sprods.length; s++)
274     {
275       sprods[s] = (seqs.getSequenceAt(s)).deriveSequence();
276       if (dataset.getSequences() == null
277               || !dataset.getSequences().contains(
278                       sprods[s].getDatasetSequence()))
279       {
280         dataset.addSequence(sprods[s].getDatasetSequence());
281       }
282       sprods[s].updatePDBIds();
283     }
284     Alignment al = new Alignment(sprods);
285     al.setDataset(dataset);
286     return al;
287   }
288
289   public CrossRefAction(AlignFrame alignFrame, SequenceI[] sel,
290           boolean _odna, String source)
291   {
292     this.alignFrame = alignFrame;
293     this.sel = sel;
294     this._odna = _odna;
295     this.source = source;
296   }
297
298   public static CrossRefAction showProductsFor(final SequenceI[] sel,
299           final boolean _odna, final String source,
300           final AlignFrame alignFrame)
301   {
302     return new CrossRefAction(alignFrame, sel, _odna, source);
303   }
304
305 }