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