JAL-4342 test and patch checking annotation csv export contains the annotations on...
[jalview.git] / src / jalview / gui / AnnotationExporter.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 java.awt.Color;
24 import java.awt.Dimension;
25 import java.awt.event.ActionEvent;
26 import java.awt.event.ActionListener;
27 import java.io.FileWriter;
28 import java.io.PrintWriter;
29 import java.util.Locale;
30
31 import javax.swing.BoxLayout;
32 import javax.swing.ButtonGroup;
33 import javax.swing.JButton;
34 import javax.swing.JCheckBox;
35 import javax.swing.JInternalFrame;
36 import javax.swing.JLabel;
37 import javax.swing.JLayeredPane;
38 import javax.swing.JPanel;
39 import javax.swing.JRadioButton;
40 import javax.swing.SwingConstants;
41
42 import jalview.api.FeatureRenderer;
43 import jalview.bin.Cache;
44 import jalview.datamodel.AlignmentAnnotation;
45 import jalview.datamodel.SequenceI;
46 import jalview.io.AnnotationFile;
47 import jalview.io.FeaturesFile;
48 import jalview.io.JalviewFileChooser;
49 import jalview.io.JalviewFileView;
50 import jalview.util.MessageManager;
51
52 /**
53  * 
54  * GUI dialog for exporting features or alignment annotations depending upon
55  * which method is called.
56  * 
57  * @author AMW
58  * 
59  */
60 public class AnnotationExporter extends JPanel
61 {
62   private JInternalFrame frame;
63
64   private AlignmentPanel ap;
65
66   /*
67    * true if exporting features, false if exporting annotations
68    */
69   private boolean exportFeatures = true;
70
71   private AlignmentAnnotation[] annotations;
72
73   private boolean wholeView;
74
75   /*
76    * option to export linked (CDS/peptide) features when shown 
77    * on the alignment, converted to this alignment's coordinates
78    */
79   private JCheckBox includeLinkedFeatures;
80
81   /*
82    * output format option shown for feature export
83    */
84   JRadioButton GFFFormat = new JRadioButton();
85
86   /*
87    * output format option shown for annotation export
88    */
89   JRadioButton CSVFormat = new JRadioButton();
90
91   private JPanel linkedFeaturesPanel;
92
93   /**
94    * Constructor
95    * 
96    * @param panel
97    */
98   public AnnotationExporter(AlignmentPanel panel)
99   {
100     this.ap = panel;
101     try
102     {
103       jbInit();
104     } catch (Exception ex)
105     {
106       ex.printStackTrace();
107     }
108
109     frame = new JInternalFrame();
110     frame.setFrameIcon(null);
111     frame.setContentPane(this);
112     frame.setLayer(JLayeredPane.PALETTE_LAYER);
113     Dimension preferredSize = frame.getPreferredSize();
114     Desktop.addInternalFrame(frame, "", true, preferredSize.width,
115             preferredSize.height, true, true);
116   }
117
118   /**
119    * Configures the dialog for options to export visible features. If from a
120    * split frame panel showing linked features, make the option to include these
121    * in the export visible.
122    */
123   public void exportFeatures()
124   {
125     exportFeatures = true;
126     CSVFormat.setVisible(false);
127     if (ap.av.isShowComplementFeatures())
128     {
129       linkedFeaturesPanel.setVisible(true);
130       frame.pack();
131     }
132     frame.setTitle(MessageManager.getString("label.export_features"));
133   }
134
135   /**
136    * Configures the dialog for options to export all visible annotations
137    */
138   public void exportAnnotations()
139   {
140     boolean showAnnotation = ap.av.isShowAnnotation();
141     exportAnnotation(showAnnotation ? null
142             : ap.av.getAlignment().getAlignmentAnnotation(), true);
143   }
144
145   /**
146    * Configures the dialog for options to export the given annotation row
147    * 
148    * @param toExport
149    */
150   public void exportAnnotation(AlignmentAnnotation toExport)
151   {
152     exportAnnotation(new AlignmentAnnotation[] { toExport }, false);
153   }
154
155   private void exportAnnotation(AlignmentAnnotation[] toExport,
156           boolean forWholeView)
157   {
158     wholeView = forWholeView;
159     annotations = toExport;
160     exportFeatures = false;
161     GFFFormat.setVisible(false);
162     CSVFormat.setVisible(true);
163     frame.setTitle(MessageManager.getString("label.export_annotations"));
164   }
165
166   public void setExportAsCSV()
167   {
168     if (CSVFormat != null)
169     {
170       CSVFormat.setSelected(true);
171     }
172   }
173
174   private void toFile_actionPerformed()
175   {
176     // TODO: JAL-3048 JalviewFileChooser - Save option
177     JalviewFileChooser chooser = new JalviewFileChooser(
178             Cache.getProperty("LAST_DIRECTORY"));
179
180     chooser.setFileView(new JalviewFileView());
181     chooser.setDialogTitle(exportFeatures
182             ? MessageManager.getString("label.save_features_to_file")
183             : MessageManager.getString("label.save_annotation_to_file"));
184     chooser.setToolTipText(MessageManager.getString("action.save"));
185
186     int value = chooser.showSaveDialog(this);
187
188     if (value == JalviewFileChooser.APPROVE_OPTION)
189     {
190       String text = getText();
191
192       try
193       {
194         PrintWriter out = new PrintWriter(
195                 new FileWriter(chooser.getSelectedFile()));
196         out.print(text);
197         out.close();
198       } catch (Exception ex)
199       {
200         ex.printStackTrace();
201       }
202     }
203
204     close_actionPerformed();
205   }
206
207   /**
208    * Answers the text to output for either Features (in GFF or Jalview format)
209    * or Annotations (in CSV or Jalview format)
210    * 
211    * @return
212    */
213   public String getText()
214   {
215     return exportFeatures ? getFeaturesText() : getAnnotationsText();
216   }
217
218   /**
219    * Returns the text contents for output of annotations in either CSV or
220    * Jalview format
221    * 
222    * @return
223    */
224   public String getAnnotationsText()
225   {
226     String text;
227     if (CSVFormat.isSelected())
228     {
229       text = new AnnotationFile().printCSVAnnotations(
230               wholeView ? ap.av.getAlignment().getAlignmentAnnotation()
231                       : annotations);
232     }
233     else
234     {
235       if (wholeView)
236       {
237         text = new AnnotationFile().printAnnotationsForView(ap.av);
238       }
239       else
240       {
241         text = new AnnotationFile().printAnnotations(annotations, null,
242                 null);
243       }
244     }
245     return text;
246   }
247
248   /**
249    * Returns the text contents for output of features in either GFF or Jalview
250    * format
251    * 
252    * @return
253    */
254   public String getFeaturesText()
255   {
256     String text;
257     SequenceI[] sequences = ap.av.getAlignment().getSequencesArray();
258     boolean includeNonPositional = ap.av.isShowNPFeats();
259
260     FeaturesFile formatter = new FeaturesFile();
261     final FeatureRenderer fr = ap.getFeatureRenderer();
262     boolean includeComplement = includeLinkedFeatures.isSelected();
263
264     if (GFFFormat.isSelected())
265     {
266       text = formatter.printGffFormat(sequences, fr, includeNonPositional,
267               includeComplement);
268     }
269     else
270     {
271       text = formatter.printJalviewFormat(sequences, fr,
272               includeNonPositional, includeComplement);
273     }
274     return text;
275   }
276
277   private void toTextbox_actionPerformed()
278   {
279     CutAndPasteTransfer cap = new CutAndPasteTransfer();
280
281     try
282     {
283       String text = getText();
284       cap.setText(text);
285       Desktop.addInternalFrame(cap, (exportFeatures ? MessageManager
286               .formatMessage("label.features_for_params", new String[]
287               { ap.alignFrame.getTitle() })
288               : MessageManager.formatMessage("label.annotations_for_params",
289                       new String[]
290                       { ap.alignFrame.getTitle() })),
291               600, 500);
292     } catch (OutOfMemoryError oom)
293     {
294       new OOMWarning((exportFeatures ? MessageManager.formatMessage(
295               "label.generating_features_for_params", new String[]
296               { ap.alignFrame.getTitle() })
297               : MessageManager.formatMessage(
298                       "label.generating_annotations_for_params",
299                       new String[]
300                       { ap.alignFrame.getTitle() })),
301               oom);
302       cap.dispose();
303     }
304
305     close_actionPerformed();
306   }
307
308   private void close_actionPerformed()
309   {
310     try
311     {
312       frame.setClosed(true);
313     } catch (java.beans.PropertyVetoException ex)
314     {
315     }
316   }
317
318   /**
319    * Adds widgets to the panel
320    * 
321    * @throws Exception
322    */
323   private void jbInit() throws Exception
324   {
325     this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
326     this.setBackground(Color.white);
327
328     JPanel formatPanel = buildFormatOptionsPanel();
329     JPanel linkedFeatures = buildLinkedFeaturesPanel();
330     JPanel actionsPanel = buildActionsPanel();
331
332     this.add(formatPanel);
333     this.add(linkedFeatures);
334     this.add(actionsPanel);
335   }
336
337   /**
338    * Builds a panel with a checkbox for the option to export linked
339    * (CDS/peptide) features. This is hidden by default, and only made visible if
340    * exporting features from a split frame panel which is configured to show
341    * linked features.
342    * 
343    * @return
344    */
345   private JPanel buildLinkedFeaturesPanel()
346   {
347     linkedFeaturesPanel = new JPanel();
348     linkedFeaturesPanel.setOpaque(false);
349
350     boolean nucleotide = ap.av.isNucleotide();
351     String complement = nucleotide ? MessageManager
352             .getString("label.protein").toLowerCase(Locale.ROOT) : "CDS";
353     JLabel label = new JLabel(MessageManager
354             .formatMessage("label.include_linked_features", complement));
355     label.setHorizontalAlignment(SwingConstants.TRAILING);
356     String tooltip = MessageManager
357             .formatMessage("label.include_linked_tooltip", complement);
358     label.setToolTipText(JvSwingUtils.wrapTooltip(true, tooltip));
359
360     includeLinkedFeatures = new JCheckBox();
361     linkedFeaturesPanel.add(label);
362     linkedFeaturesPanel.add(includeLinkedFeatures);
363     linkedFeaturesPanel.setVisible(false);
364
365     return linkedFeaturesPanel;
366   }
367
368   /**
369    * Builds the panel with to File or Textbox or Close actions
370    * 
371    * @return
372    */
373   JPanel buildActionsPanel()
374   {
375     JPanel actionsPanel = new JPanel();
376     actionsPanel.setOpaque(false);
377
378     JButton toFile = new JButton(MessageManager.getString("label.to_file"));
379     toFile.addActionListener(new ActionListener()
380     {
381       @Override
382       public void actionPerformed(ActionEvent e)
383       {
384         toFile_actionPerformed();
385       }
386     });
387     JButton toTextbox = new JButton(
388             MessageManager.getString("label.to_textbox"));
389     toTextbox.addActionListener(new ActionListener()
390     {
391       @Override
392       public void actionPerformed(ActionEvent e)
393       {
394         toTextbox_actionPerformed();
395       }
396     });
397     JButton close = new JButton(MessageManager.getString("action.close"));
398     close.addActionListener(new ActionListener()
399     {
400       @Override
401       public void actionPerformed(ActionEvent e)
402       {
403         close_actionPerformed();
404       }
405     });
406
407     actionsPanel.add(toFile);
408     actionsPanel.add(toTextbox);
409     actionsPanel.add(close);
410
411     return actionsPanel;
412   }
413
414   /**
415    * Builds the panel with options to output in Jalview, GFF or CSV format. GFF
416    * is only made visible when exporting features, CSV only when exporting
417    * annotation.
418    * 
419    * @return
420    */
421   JPanel buildFormatOptionsPanel()
422   {
423     JPanel formatPanel = new JPanel();
424     // formatPanel.setBorder(BorderFactory.createEtchedBorder());
425     formatPanel.setOpaque(false);
426
427     JRadioButton jalviewFormat = new JRadioButton("Jalview");
428     jalviewFormat.setOpaque(false);
429     jalviewFormat.setSelected(true);
430     GFFFormat.setOpaque(false);
431     GFFFormat.setText("GFF");
432     CSVFormat.setOpaque(false);
433     CSVFormat.setText(MessageManager.getString("label.csv_spreadsheet"));
434
435     ButtonGroup buttonGroup = new ButtonGroup();
436     buttonGroup.add(jalviewFormat);
437     buttonGroup.add(GFFFormat);
438     buttonGroup.add(CSVFormat);
439
440     JLabel format = new JLabel(
441             MessageManager.getString("action.format") + " ");
442     format.setHorizontalAlignment(SwingConstants.TRAILING);
443
444     formatPanel.add(format);
445     formatPanel.add(jalviewFormat);
446     formatPanel.add(GFFFormat);
447     formatPanel.add(CSVFormat);
448
449     return formatPanel;
450   }
451 }