94c135914d448ac92c92a1e2635ff69de116205f
[jalview.git] / forester / java / src / org / forester / archaeopteryx / tools / PhyloInferenceDialog.java
1 // $Id:
2 // forester -- software libraries and applications
3 // for genomics and evolutionary biology research.
4 //
5 // Copyright (C) 2010 Christian M Zmasek
6 // Copyright (C) 2010 Sanford-Burnham Medical Research Institute
7 // All rights reserved
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 //
23 // Contact: phylosoft @ gmail . com
24 // WWW: https://sites.google.com/site/cmzmasek/home/software/forester
25
26 package org.forester.archaeopteryx.tools;
27
28 import java.awt.Color;
29 import java.awt.FlowLayout;
30 import java.awt.Frame;
31 import java.awt.event.ActionEvent;
32 import java.awt.event.ActionListener;
33 import java.util.List;
34
35 import javax.swing.BoxLayout;
36 import javax.swing.ButtonGroup;
37 import javax.swing.JButton;
38 import javax.swing.JCheckBox;
39 import javax.swing.JDialog;
40 import javax.swing.JFormattedTextField;
41 import javax.swing.JLabel;
42 import javax.swing.JOptionPane;
43 import javax.swing.JPanel;
44 import javax.swing.JRadioButton;
45 import javax.swing.JTextField;
46 import javax.swing.border.Border;
47 import javax.swing.border.LineBorder;
48
49 import org.forester.archaeopteryx.AptxUtil;
50 import org.forester.archaeopteryx.MainFrameApplication;
51 import org.forester.evoinference.distance.PairwiseDistanceCalculator.PWD_DISTANCE_METHOD;
52 import org.forester.sequence.MolecularSequence;
53 import org.forester.util.BasicDescriptiveStatistics;
54 import org.forester.util.DescriptiveStatistics;
55
56 public class PhyloInferenceDialog extends JDialog implements ActionListener {
57
58     private static final long                  serialVersionUID = 8337543508238133614L;
59     private final JPanel                       _pnl;
60     private final JButton                      _launch_btn;
61     private final JButton                      _cancel_btn;
62     private final JFormattedTextField          _bootstrap_tf;
63     private final JCheckBox                    _bootstrap_cb;
64     private final PhylogeneticInferenceOptions _opts;
65     private JTextField                         _input_msa_file_tf;
66     private JButton                            _select_input_msa_btn;
67     private final MainFrameApplication         _parent_frame;
68     private JTextField                         _msa_length_tf;
69     private JTextField                         _msa_size_tf;
70     private JTextField                         _msa_type_tf;
71     private final JRadioButton                 _distance_calc_kimura_rb;
72     private final JRadioButton                 _distance_calc_poisson_rb;
73     private final JRadioButton                 _distance_calc_fract_dissimilarity_rb;
74     private int                                _value           = JOptionPane.CANCEL_OPTION;
75     private JTextField                         _input_seqs_tf;
76     private JButton                            _select_input_seqs_btn;
77     private JTextField                         _input_seqs_number_tf;
78     private JTextField                         _input_seqs_median_length_tf;
79     private JTextField                         _input_seqs_min_length_tf;
80     private JTextField                         _input_seqs_max_length_tf;
81     private JTextField                         _input_seqs_type_tf;
82     private JTextField                         _mafft_paramenters_tf;
83     private JTextField                         _msa_processing_max_allowed_gap_ratio_tf;
84     private JTextField                         _msa_processing_min_allowed_length_tf;
85     private JTextField                         _random_seed_tf;
86     private JCheckBox                          _execute_msa_processing_cb;
87     private JCheckBox                          _msa_processing_remove_all_gap_columns_cb;
88     private JCheckBox                          _mafft_cb;
89     private JCheckBox                          _save_pwd_file_cb;
90     private JCheckBox                          _save_processed_msa_cb;
91     private JCheckBox                          _save_original_msa_cb;
92     private JTextField                         _pwd_outfile_tf;
93     private JTextField                         _processed_msa_outfile_tf;
94     private JTextField                         _original_msa_outfile_tf;
95
96     public PhyloInferenceDialog( final MainFrameApplication frame,
97                                  final PhylogeneticInferenceOptions options,
98                                  final boolean from_unaligned_seqs ) {
99         super( (Frame) null, true );
100         setVisible( false );
101         _parent_frame = frame;
102         _opts = options;
103         _pnl = new JPanel();
104         getContentPane().add( _pnl );
105         final BoxLayout box_layout = new BoxLayout( _pnl, BoxLayout.PAGE_AXIS );
106         _pnl.setLayout( box_layout );
107         if ( from_unaligned_seqs ) {
108             setTitle( "Phylogenetic Inference (including multiple sequence alignment)" );
109             final JPanel inputfile_pnl_1 = new JPanel();
110             final JPanel inputfile_pnl_2 = new JPanel();
111             final JPanel inputfile_pnl_3 = new JPanel();
112             final JPanel inputfile_pnl_4 = new JPanel();
113             inputfile_pnl_1.setLayout( new FlowLayout() );
114             inputfile_pnl_2.setLayout( new FlowLayout() );
115             inputfile_pnl_3.setLayout( new FlowLayout() );
116             inputfile_pnl_4.setLayout( new FlowLayout() );
117             inputfile_pnl_1.add( new JLabel( "Input Sequence File:" ) );
118             inputfile_pnl_1.add( _input_seqs_tf = new JTextField() );
119             inputfile_pnl_1.add( _select_input_seqs_btn = new JButton( "Select Input File" ) );
120             inputfile_pnl_2.add( new JLabel( "Sequences: " ) );
121             inputfile_pnl_2.add( new JLabel( "Number of Sequences:" ) );
122             inputfile_pnl_2.add( _input_seqs_number_tf = new JTextField() );
123             inputfile_pnl_2.add( new JLabel( "Length: median:" ) );
124             inputfile_pnl_2.add( _input_seqs_median_length_tf = new JTextField() );
125             inputfile_pnl_2.add( new JLabel( "min:" ) );
126             inputfile_pnl_2.add( _input_seqs_min_length_tf = new JTextField() );
127             inputfile_pnl_2.add( new JLabel( "max:" ) );
128             inputfile_pnl_2.add( _input_seqs_max_length_tf = new JTextField() );
129             inputfile_pnl_2.add( new JLabel( "Type:" ) );
130             inputfile_pnl_2.add( _input_seqs_type_tf = new JTextField() );
131             inputfile_pnl_3.add( _mafft_cb = new JCheckBox( "MAFFT" ) );
132             inputfile_pnl_3.add( new JLabel( "Parameters: " ) );
133             inputfile_pnl_3.add( _mafft_paramenters_tf = new JTextField() );
134             _input_seqs_median_length_tf.setColumns( 4 );
135             _input_seqs_min_length_tf.setColumns( 4 );
136             _input_seqs_max_length_tf.setColumns( 4 );
137             _input_seqs_number_tf.setColumns( 4 );
138             _input_seqs_type_tf.setColumns( 2 );
139             _input_seqs_tf.setColumns( 20 );
140             _input_seqs_tf.setEditable( false );
141             _input_seqs_median_length_tf.setEditable( false );
142             _input_seqs_min_length_tf.setEditable( false );
143             _input_seqs_max_length_tf.setEditable( false );
144             _input_seqs_number_tf.setEditable( false );
145             _input_seqs_type_tf.setEditable( false );
146             _mafft_paramenters_tf.setColumns( 26 );
147             _mafft_paramenters_tf.setText( "--maxiterate 1000 --localpair" );
148             _select_input_seqs_btn.addActionListener( this );
149             _pnl.add( inputfile_pnl_1 );
150             _pnl.add( inputfile_pnl_2 );
151             _pnl.add( inputfile_pnl_3 );
152             _pnl.add( inputfile_pnl_4 );
153         }
154         else {
155             setTitle( "Phylogenetic Inference (from already aligned sequences) " );
156             // Inputfile (MSA):
157             final JPanel inputfile_pnl_1 = new JPanel();
158             final JPanel inputfile_pnl_2 = new JPanel();
159             inputfile_pnl_1.setLayout( new FlowLayout() );
160             inputfile_pnl_2.setLayout( new FlowLayout() );
161             inputfile_pnl_1.add( new JLabel( "Input MSA File:" ) );
162             inputfile_pnl_1.add( _input_msa_file_tf = new JTextField() );
163             inputfile_pnl_1.add( _select_input_msa_btn = new JButton( "Select Input File" ) );
164             inputfile_pnl_2.add( new JLabel( "MSA: " ) );
165             inputfile_pnl_2.add( new JLabel( "Number of Sequences:" ) );
166             inputfile_pnl_2.add( _msa_size_tf = new JTextField() );
167             inputfile_pnl_2.add( new JLabel( "Length:" ) );
168             inputfile_pnl_2.add( _msa_length_tf = new JTextField() );
169             inputfile_pnl_2.add( new JLabel( "Type:" ) );
170             inputfile_pnl_2.add( _msa_type_tf = new JTextField() );
171             _msa_length_tf.setColumns( 4 );
172             _msa_size_tf.setColumns( 4 );
173             _msa_type_tf.setColumns( 2 );
174             _input_msa_file_tf.setColumns( 20 );
175             _input_msa_file_tf.setEditable( false );
176             _msa_length_tf.setEditable( false );
177             _msa_size_tf.setEditable( false );
178             _msa_type_tf.setEditable( false );
179             _select_input_msa_btn.addActionListener( this );
180             _pnl.add( inputfile_pnl_1 );
181             _pnl.add( inputfile_pnl_2 );
182         }
183         //
184         final JPanel inputfile_pnl_4 = new JPanel();
185         inputfile_pnl_4.setLayout( new FlowLayout() );
186         inputfile_pnl_4.add( new JLabel( "MSA Processing: " ) );
187         inputfile_pnl_4.add( _execute_msa_processing_cb = new JCheckBox( "Process MSA" ) );
188         inputfile_pnl_4.add( _msa_processing_remove_all_gap_columns_cb = new JCheckBox( "Remove all gap columns" ) );
189         inputfile_pnl_4.add( new JLabel( "Max allowed gap ratio: " ) );
190         inputfile_pnl_4.add( _msa_processing_max_allowed_gap_ratio_tf = new JTextField() );
191         inputfile_pnl_4.add( new JLabel( "Min allowed non-gap sequence length: " ) );
192         inputfile_pnl_4.add( _msa_processing_min_allowed_length_tf = new JTextField() );
193         _msa_processing_max_allowed_gap_ratio_tf.setColumns( 4 );
194         _msa_processing_min_allowed_length_tf.setColumns( 4 );
195         final Border b = new LineBorder( Color.DARK_GRAY );
196         inputfile_pnl_4.setBorder( b );
197         _pnl.add( inputfile_pnl_4 );
198         //
199         // Distance calculation:
200         // TODO if type==AA...
201         final JPanel distance_calc_pnl_1 = new JPanel();
202         distance_calc_pnl_1.setLayout( new FlowLayout() );
203         distance_calc_pnl_1.add( new JLabel( "Distance calculation:" ) );
204         distance_calc_pnl_1.add( _distance_calc_kimura_rb = new JRadioButton( "Kimura correction" ) );
205         distance_calc_pnl_1.add( _distance_calc_poisson_rb = new JRadioButton( "Poisson" ) );
206         distance_calc_pnl_1
207         .add( _distance_calc_fract_dissimilarity_rb = new JRadioButton( "Fractional dissimilarity" ) );
208         final ButtonGroup distance_calc_group_1 = new ButtonGroup();
209         distance_calc_group_1.add( _distance_calc_kimura_rb );
210         distance_calc_group_1.add( _distance_calc_poisson_rb );
211         distance_calc_group_1.add( _distance_calc_fract_dissimilarity_rb );
212         _pnl.add( distance_calc_pnl_1 );
213         // Bootstrap resampling:
214         final JPanel bootstrap_pnl = new JPanel();
215         bootstrap_pnl.setLayout( new FlowLayout() );
216         bootstrap_pnl.add( _bootstrap_cb = new JCheckBox( "Perform Bootstrap Resampling" ) );
217         bootstrap_pnl.add( new JLabel( "Number of Bootstrap Samples:" ) );
218         bootstrap_pnl.add( _bootstrap_tf = new JFormattedTextField( AptxUtil.createMaskFormatter( "###" ) ) );
219         _bootstrap_tf.setColumns( 4 );
220         // TODO see
221         // http://download.oracle.com/javase/tutorial/uiswing/components/formattedtextfield.html
222         // _bootstrap_tf.setColumns( 4 );
223         bootstrap_pnl.add( new JLabel( "Random Seed:" ) );
224         bootstrap_pnl.add( _random_seed_tf = new JTextField() );
225         _random_seed_tf.setColumns( 4 );
226         _pnl.add( bootstrap_pnl );
227         final JPanel launch_pnl = new JPanel();
228         launch_pnl.setLayout( new FlowLayout() );
229         _launch_btn = new JButton( "Go!" );
230         _launch_btn.addActionListener( this );
231         launch_pnl.add( _launch_btn );
232         _cancel_btn = new JButton( "Cancel" );
233         _cancel_btn.addActionListener( this );
234         launch_pnl.add( _cancel_btn );
235         _pnl.add( launch_pnl );
236         initializeValues( from_unaligned_seqs );
237         pack();
238         setLocationRelativeTo( getParentFrame().getThisFrame() );
239         setResizable( false );
240     }
241
242  
243
244     @Override
245     public void actionPerformed( final ActionEvent e ) {
246         if ( e.getSource() == _select_input_msa_btn ) {
247             readInputFile();
248         }
249         else if ( e.getSource() == _select_input_seqs_btn ) {
250             readInputSeqsFile();
251         }
252         else if ( e.getSource() == _launch_btn ) {
253             launch();
254         }
255         else if ( e.getSource() == _cancel_btn ) {
256             cancel();
257         }
258     }
259
260     public void activate() {
261         setVisible( true );
262     }
263
264     private MainFrameApplication getParentFrame() {
265         return _parent_frame;
266     }
267
268     public PhylogeneticInferenceOptions getPhylogeneticInferenceOptions() {
269         return _opts;
270     }
271
272     public int getValue() {
273         return _value;
274     }
275
276     private void initializeValues( final boolean from_unaligned_seqs ) {
277         _value = JOptionPane.CANCEL_OPTION;
278         if ( from_unaligned_seqs ) {
279             updateSeqsItems();
280         }
281         else {
282             updateMsaItems();
283         }
284         updateMsaProcessingItem();
285         updateDistanceCalcMethod();
286         _bootstrap_tf.setText( getPhylogeneticInferenceOptions().getBootstrapSamples() + "" );
287         _random_seed_tf.setText( getPhylogeneticInferenceOptions().getRandomNumberGeneratorSeed() + "" );
288     }
289
290     private void launch() {
291         processPerformBootstrapResampling();
292         if ( _bootstrap_cb.isSelected() ) {
293             processBootstrapSamplesNumber();
294             processRandomNumberGeneratorSeed();
295         }
296         if ( true ) {
297             //TODO
298             processMsaProcessing();
299         }
300         processDistanceCalcMethod();
301         processMsaPrgParameters();
302         setVisible( false );
303         _value = JOptionPane.OK_OPTION;
304     }
305
306     private void cancel() {
307         setVisible( false );
308         _value = JOptionPane.CANCEL_OPTION;
309     }
310
311     private void processBootstrapSamplesNumber() {
312         int bootstrap_samples = 0;
313         try {
314             bootstrap_samples = Integer.parseInt( _bootstrap_tf.getText().trim() );
315         }
316         catch ( final NumberFormatException e ) {
317             // JOptionPane.showMessageDialog( this, "Could not parse number of bootstrap resamplings from: " +  _bootstrap_tf.getText().trim(), "User Error", JOptionPane.ERROR_MESSAGE );
318             return;
319         }
320         if ( bootstrap_samples >= 0 ) {
321             getPhylogeneticInferenceOptions().setBootstrapSamples( bootstrap_samples );
322         }
323     }
324
325     private void processRandomNumberGeneratorSeed() {
326         long seed = PhylogeneticInferenceOptions.RANDOM_NUMBER_SEED_DEFAULT;
327         try {
328             seed = Long.parseLong( _random_seed_tf.getText().trim() );
329         }
330         catch ( final NumberFormatException e ) {
331             return;
332         }
333         getPhylogeneticInferenceOptions().setRandomNumberGeneratorSeed( seed );
334     }
335
336     private void processMsaProcessing() {
337         getPhylogeneticInferenceOptions().setExecuteMsaProcessing( _execute_msa_processing_cb.isSelected() );
338         getPhylogeneticInferenceOptions()
339         .setMsaProcessingRemoveAllGapColumns( _msa_processing_remove_all_gap_columns_cb.isSelected() );
340         int min_length = -1;
341         try {
342             min_length = Integer.parseInt( _msa_processing_min_allowed_length_tf.getText().trim() );
343         }
344         catch ( final NumberFormatException e ) {
345             min_length = -1;
346         }
347         if ( min_length > 0 ) {
348             getPhylogeneticInferenceOptions().setMsaProcessingMinAllowedLength( min_length );
349         }
350         double msa_processing_max_allowed_gap_ratio = -1.0;
351         try {
352             msa_processing_max_allowed_gap_ratio = Double.parseDouble( _msa_processing_max_allowed_gap_ratio_tf
353                                                                        .getText().trim() );
354         }
355         catch ( final NumberFormatException e ) {
356             msa_processing_max_allowed_gap_ratio = -1.0;
357         }
358         if ( ( msa_processing_max_allowed_gap_ratio >= 0.0 ) && ( msa_processing_max_allowed_gap_ratio <= 1.0 ) ) {
359             getPhylogeneticInferenceOptions().setMsaProcessingMaxAllowedGapRatio( msa_processing_max_allowed_gap_ratio );
360         }
361     }
362
363     private void processDistanceCalcMethod() {
364         if ( ( _distance_calc_kimura_rb != null ) && _distance_calc_kimura_rb.isSelected() ) {
365             getPhylogeneticInferenceOptions().setPwdDistanceMethod( PWD_DISTANCE_METHOD.KIMURA_DISTANCE );
366         }
367         else if ( ( _distance_calc_poisson_rb != null ) && _distance_calc_poisson_rb.isSelected() ) {
368             getPhylogeneticInferenceOptions().setPwdDistanceMethod( PWD_DISTANCE_METHOD.POISSON_DISTANCE );
369         }
370         else if ( ( _distance_calc_fract_dissimilarity_rb != null )
371                 && _distance_calc_fract_dissimilarity_rb.isSelected() ) {
372             getPhylogeneticInferenceOptions().setPwdDistanceMethod( PWD_DISTANCE_METHOD.FRACTIONAL_DISSIMILARITY );
373         }
374     }
375
376     private void processPerformBootstrapResampling() {
377         getPhylogeneticInferenceOptions().setPerformBootstrapResampling( _bootstrap_cb.isSelected() );
378     }
379
380     private void processMsaPrgParameters() {
381         if ( _mafft_paramenters_tf != null ) {
382             getPhylogeneticInferenceOptions().setMsaPrgParameters( _mafft_paramenters_tf.getText() );
383         }
384     }
385
386     private void readInputFile() {
387         getParentFrame().readMsaFromFile();
388         updateMsaItems();
389     }
390
391     private void readInputSeqsFile() {
392         getParentFrame().readSeqsFromFileforPI();
393         updateSeqsItems();
394     }
395
396     private void updateDistanceCalcMethod() {
397         switch ( getPhylogeneticInferenceOptions().getPwdDistanceMethod() ) {
398             case KIMURA_DISTANCE:
399                 _distance_calc_kimura_rb.setSelected( true );
400                 break;
401             case POISSON_DISTANCE:
402                 _distance_calc_poisson_rb.setSelected( true );
403                 break;
404             case FRACTIONAL_DISSIMILARITY:
405                 _distance_calc_fract_dissimilarity_rb.setSelected( true );
406                 break;
407             default:
408                 throw new RuntimeException( "invalid distance calc method" );
409         }
410     }
411
412     private void updateMsaProcessingItem() {
413         _execute_msa_processing_cb.setSelected( getPhylogeneticInferenceOptions().isExecuteMsaProcessing() );
414         _msa_processing_remove_all_gap_columns_cb.setSelected( getPhylogeneticInferenceOptions()
415                                                                .isMsaProcessingRemoveAllGapColumns() );
416         if ( _opts.getMsaProcessingMaxAllowedGapRatio() > 0 ) {
417             _msa_processing_max_allowed_gap_ratio_tf.setText( _opts.getMsaProcessingMaxAllowedGapRatio() + "" );
418         }
419         if ( _opts.getMsaProcessingMinAllowedLength() > 0 ) {
420             _msa_processing_min_allowed_length_tf.setText( _opts.getMsaProcessingMinAllowedLength() + "" );
421         }
422     }
423
424     private void updateMsaItems() {
425         if ( getParentFrame().getMsa() != null ) {
426             _input_msa_file_tf.setText( getParentFrame().getMsaFile().toString() );
427             _msa_length_tf.setText( getParentFrame().getMsa().getLength() + "" );
428             _msa_size_tf.setText( getParentFrame().getMsa().getNumberOfSequences() + "" );
429             _msa_type_tf.setText( getParentFrame().getMsa().getType() + "" );
430             _input_msa_file_tf.setEnabled( true );
431             _msa_length_tf.setEnabled( true );
432             _msa_size_tf.setEnabled( true );
433             _msa_type_tf.setEnabled( true );
434             _launch_btn.setEnabled( true );
435         }
436         else {
437             _input_msa_file_tf.setText( "" );
438             _msa_length_tf.setText( "" );
439             _msa_size_tf.setText( "" );
440             _msa_type_tf.setText( "" );
441             _input_msa_file_tf.setEnabled( false );
442             _msa_length_tf.setEnabled( false );
443             _msa_size_tf.setEnabled( false );
444             _msa_type_tf.setEnabled( false );
445             _launch_btn.setEnabled( false );
446         }
447     }
448
449     private void updateSeqsItems() {
450         if ( getParentFrame().getSeqs() != null ) {
451             final DescriptiveStatistics stats = calcSequenceStats( getParentFrame().getSeqs() );
452             _input_seqs_tf.setText( getParentFrame().getSeqsFile().toString() );
453             _input_seqs_median_length_tf.setText( ( int ) stats.median() + "" );
454             _input_seqs_min_length_tf.setText( ( int ) stats.getMin() + "" );
455             _input_seqs_max_length_tf.setText( ( int ) stats.getMax() + "" );
456             _input_seqs_number_tf.setText( getParentFrame().getSeqs().size() + "" );
457             _input_seqs_type_tf.setText( getParentFrame().getSeqs().get( 0 ).getType() + "" );
458             _input_seqs_tf.setEnabled( true );
459             _input_seqs_median_length_tf.setEnabled( true );
460             _input_seqs_min_length_tf.setEnabled( true );
461             _input_seqs_max_length_tf.setEnabled( true );
462             _input_seqs_number_tf.setEnabled( true );
463             _input_seqs_type_tf.setEnabled( true );
464             _launch_btn.setEnabled( true );
465         }
466         else {
467             _input_seqs_tf.setText( "" );
468             _input_seqs_median_length_tf.setText( "" );
469             _input_seqs_min_length_tf.setText( "" );
470             _input_seqs_max_length_tf.setText( "" );
471             _input_seqs_number_tf.setText( "" );
472             _input_seqs_type_tf.setText( "" );
473             _input_seqs_tf.setEnabled( false );
474             _input_seqs_median_length_tf.setEnabled( false );
475             _input_seqs_min_length_tf.setEnabled( false );
476             _input_seqs_max_length_tf.setEnabled( false );
477             _input_seqs_number_tf.setEnabled( false );
478             _input_seqs_type_tf.setEnabled( false );
479             _launch_btn.setEnabled( false );
480         }
481     }
482
483     DescriptiveStatistics calcSequenceStats( final List<MolecularSequence> seqs ) {
484         final DescriptiveStatistics stats = new BasicDescriptiveStatistics();
485         for( final MolecularSequence s : seqs ) {
486             stats.addValue( s.getLength() );
487         }
488         return stats;
489     }
490 }