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