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