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