/* Selector.java
 *
 * created: Tue Apr 11 2000
 *
 * This file is part of Artemis
 *
 * Copyright (C) 2000,2001,2002  Genome Research Limited
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * $Header: /cvsroot/pathsoft/artemis/uk/ac/sanger/artemis/components/Selector.java,v 1.1 2004/06/09 09:47:46 tjc Exp $
 */

package uk.ac.sanger.artemis.components;

import uk.ac.sanger.artemis.*;
import uk.ac.sanger.artemis.sequence.AminoAcidSequence;

import uk.ac.sanger.artemis.io.EntryInformation;
import uk.ac.sanger.artemis.util.StringVector;

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;

/**
 *  This component allows the user to set the selection by filtering the
 *  features in an EntryGroup on key and contents.
 *
 *  @author Kim Rutherford <kmr@sanger.ac.uk>
 *  @version $Id: Selector.java,v 1.1 2004/06/09 09:47:46 tjc Exp $
 **/

public class Selector extends JFrame
    implements EntryGroupChangeListener {
  /**
   *  Create a new Selector that van set the given Selection.
   *  @param selection The Selection that the commands in the menu will
   *    operate on.
   *  @param entry_group The component will choose features from this
   *    EntryGroup.
   *  @param goto_event_source The object the we will call gotoBase () on.
   *  @param base_plot_group The BasePlotGroup associated with this JMenu -
   *    needed to call getCodonUsageAlgorithm()
   **/
  public Selector (final Selection selection,
                   final GotoEventSource goto_event_source,
                   final EntryGroup entry_group,
                   final BasePlotGroup base_plot_group) {
    super ("Artemis Feature Selector");

    this.selection = selection;
    this.entry_group = entry_group;

    final Font default_font = Options.getOptions ().getFont ();

    setFont (default_font);

    GridBagLayout gridbag = new GridBagLayout();
    getContentPane ().setLayout (gridbag);

    GridBagConstraints c = new GridBagConstraints();

    c.fill = GridBagConstraints.HORIZONTAL;
    c.anchor = GridBagConstraints.WEST;
    c.weighty = 0;
    c.gridwidth = GridBagConstraints.REMAINDER;

    final JLabel top_label = new JLabel ("Select by:");
    gridbag.setConstraints (top_label, c);
    getContentPane ().add (top_label);


    by_key_button = new JCheckBox ("Key: ", false);
    final JPanel by_key_panel = new JPanel ();
    by_key_panel.setLayout (new FlowLayout (FlowLayout.LEFT));
    by_key_panel.add (by_key_button);
    c.gridwidth = 2;
    gridbag.setConstraints (by_key_panel, c);
    getContentPane ().add (by_key_panel);

    final EntryInformation default_entry_information =
      Options.getArtemisEntryInformation ();

    key_selector = new KeyChoice (default_entry_information);
    c.gridwidth = GridBagConstraints.REMAINDER;
    gridbag.setConstraints (key_selector, c);
    getContentPane ().add (key_selector);

    key_selector.addItemListener (new ItemListener () {
      public void itemStateChanged (ItemEvent _) {
        by_key_button.setSelected (true);
      }
    });

    by_key_button.addItemListener (new ItemListener () {
      public void itemStateChanged (ItemEvent e) {
        if (!by_key_button.isSelected ()) {
          by_qualifier_button.setSelected (false);
        }
      }
    });


    by_qualifier_button = new JCheckBox ("Qualifier: ", false);
    final JPanel by_qualifier_panel = new JPanel ();
    by_qualifier_panel.setLayout (new FlowLayout (FlowLayout.LEFT));
    by_qualifier_panel.add (by_qualifier_button);
    c.gridwidth = 2;
    gridbag.setConstraints (by_qualifier_panel, c);
    getContentPane ().add (by_qualifier_panel);

    qualifier_selector = new QualifierChoice (default_entry_information,
                                              key_selector.getSelectedItem (),
                                              null);
    c.gridwidth = GridBagConstraints.REMAINDER;
    gridbag.setConstraints (qualifier_selector, c);
    getContentPane ().add (qualifier_selector);

    qualifier_selector.addItemListener (new ItemListener () {
      public void itemStateChanged (ItemEvent _) {
        by_qualifier_button.setSelected (true);
        by_key_button.setSelected (true);
      }
    });

    key_selector.addItemListener (new ItemListener () {
      public void itemStateChanged (ItemEvent _) {
        qualifier_selector.setKey (key_selector.getSelectedItem ());
      }
    });

    by_qualifier_button.addItemListener (new ItemListener () {
      public void itemStateChanged (ItemEvent e) {
        if (by_qualifier_button.isSelected ()) {
          if (!by_key_button.isSelected ()) {
            by_key_button.setSelected (true);
          }
        }
      }
    });


    final JLabel qualifier_text_label =
      new JLabel ("   Containing this text: ");
    c.gridwidth = 2;
    gridbag.setConstraints (qualifier_text_label, c);
    getContentPane ().add (qualifier_text_label);

    qualifier_text = new JTextField ("", 18);

    c.gridwidth = GridBagConstraints.REMAINDER;
    gridbag.setConstraints (qualifier_text, c);
    getContentPane ().add (qualifier_text);


    ignore_case_checkbox = new JCheckBox ("Ignore Case", true);

    final JPanel ignore_case_panel = new JPanel ();

    ignore_case_panel.setLayout (new FlowLayout (FlowLayout.LEFT));
    ignore_case_panel.add (ignore_case_checkbox);

    c.gridwidth = 2;
    gridbag.setConstraints (ignore_case_panel, c);
    getContentPane ().add (ignore_case_panel);


    partial_match_checkbox = new JCheckBox ("Allow Partial Match", true);

    final JPanel partial_match_panel = new JPanel ();

    partial_match_panel.setLayout (new FlowLayout (FlowLayout.LEFT));
    partial_match_panel.add (partial_match_checkbox);

    c.gridwidth = GridBagConstraints.REMAINDER;
    gridbag.setConstraints (partial_match_panel, c);
    getContentPane ().add (partial_match_panel);


    match_any_word_checkbox = new JCheckBox ("Match Any Word", false);

    final JPanel match_any_word_panel = new JPanel ();

    match_any_word_panel.setLayout (new FlowLayout (FlowLayout.LEFT));
    match_any_word_panel.add (match_any_word_checkbox);

    c.gridwidth = GridBagConstraints.REMAINDER;
    gridbag.setConstraints (match_any_word_panel, c);
    getContentPane ().add (match_any_word_panel);


    {
      // leave a blank line to give the user a visual clue
      final JLabel blank_label = new JLabel ("");

      gridbag.setConstraints (blank_label, c);
      getContentPane ().add (blank_label);

      final JLabel and_label = new JLabel ("And:");

      gridbag.setConstraints (and_label, c);
      getContentPane ().add (and_label);
    }


    less_than_bases_button = new JCheckBox ("Up to: ", false);
    final JPanel less_than_bases_panel = new JPanel ();
    less_than_bases_panel.setLayout (new FlowLayout (FlowLayout.LEFT));
    less_than_bases_panel.add (less_than_bases_button);
    c.gridwidth = 3;
    gridbag.setConstraints (less_than_bases_panel, c);
    getContentPane ().add (less_than_bases_panel);

    less_than_bases_text = new JTextField ("", 18);

    gridbag.setConstraints (less_than_bases_text, c);
    getContentPane ().add (less_than_bases_text);

    final JLabel less_than_bases_label = new JLabel (" bases long");

    c.gridwidth = GridBagConstraints.REMAINDER;
    gridbag.setConstraints (less_than_bases_label, c);
    getContentPane ().add (less_than_bases_label);

    {
      // leave a blank line to give the user a visual clue
      final JLabel blank_label = new JLabel ("");

      gridbag.setConstraints (blank_label, c);
      getContentPane ().add (blank_label);

      final JLabel and_label = new JLabel ("And:");

      gridbag.setConstraints (and_label, c);
      getContentPane ().add (and_label);
    }


    greater_than_bases_button = new JCheckBox ("At least: ", false);
    final JPanel greater_than_bases_panel = new JPanel ();
    greater_than_bases_panel.setLayout (new FlowLayout (FlowLayout.LEFT));
    greater_than_bases_panel.add (greater_than_bases_button);
    c.gridwidth = 2;
    gridbag.setConstraints (greater_than_bases_panel, c);
    getContentPane ().add (greater_than_bases_panel);

    greater_than_bases_text = new JTextField ("", 18);

    gridbag.setConstraints (greater_than_bases_text, c);
    getContentPane ().add (greater_than_bases_text);

    final JLabel greater_than_bases_label = new JLabel (" bases long");

    c.gridwidth = GridBagConstraints.REMAINDER;
    gridbag.setConstraints (greater_than_bases_label, c);
    getContentPane ().add (greater_than_bases_label);

    {
      // leave a blank line to give the user a visual clue
      final JLabel blank_label = new JLabel ("");

      gridbag.setConstraints (blank_label, c);
      getContentPane ().add (blank_label);

      final JLabel and_label = new JLabel ("And:");

      gridbag.setConstraints (and_label, c);
      getContentPane ().add (and_label);
    }

    less_than_exons_button = new JCheckBox ("Up to: ", false);
    final JPanel less_than_exons_panel = new JPanel ();
    less_than_exons_panel.setLayout (new FlowLayout (FlowLayout.LEFT));
    less_than_exons_panel.add (less_than_exons_button);
    c.gridwidth = 3;
    gridbag.setConstraints (less_than_exons_panel, c);
    getContentPane ().add (less_than_exons_panel);

    less_than_exons_text = new JTextField ("", 18);

    gridbag.setConstraints (less_than_exons_text, c);
    getContentPane ().add (less_than_exons_text);

    final JLabel less_than_exons_label = new JLabel (" exons long");

    c.gridwidth = GridBagConstraints.REMAINDER;
    gridbag.setConstraints (less_than_exons_label, c);
    getContentPane ().add (less_than_exons_label);

    {
      // leave a blank line to give the user a visual clue
      final JLabel blank_label = new JLabel ("");

      gridbag.setConstraints (blank_label, c);
      getContentPane ().add (blank_label);

      final JLabel and_label = new JLabel ("And:");
      
      gridbag.setConstraints (and_label, c);
      getContentPane ().add (and_label);
    }


    greater_than_exons_button = new JCheckBox ("At least: ", false);
    final JPanel greater_than_exons_panel = new JPanel ();
    greater_than_exons_panel.setLayout (new FlowLayout (FlowLayout.LEFT));
    greater_than_exons_panel.add (greater_than_exons_button);
    c.gridwidth = 2;
    gridbag.setConstraints (greater_than_exons_panel, c);
    getContentPane ().add (greater_than_exons_panel);

    greater_than_exons_text = new JTextField ("", 18);

    gridbag.setConstraints (greater_than_exons_text, c);
    getContentPane ().add (greater_than_exons_text);

    final JLabel greater_than_exons_label = new JLabel (" exons long");

    c.gridwidth = GridBagConstraints.REMAINDER;
    gridbag.setConstraints (greater_than_exons_label, c);
    getContentPane ().add (greater_than_exons_label);

    {
      // leave a blank line to give the user a visual clue
      final JLabel blank_label = new JLabel ("");

      gridbag.setConstraints (blank_label, c);
      getContentPane ().add (blank_label);

      final JLabel and_label = new JLabel ("And by:");

      gridbag.setConstraints (and_label, c);
      getContentPane ().add (and_label);
    }


    by_motif_button = new JCheckBox ("Amino acid motif: ", false);
    final JPanel by_motif_panel = new JPanel ();
    by_motif_panel.setLayout (new FlowLayout (FlowLayout.LEFT));
    by_motif_panel.add (by_motif_button);
    c.gridwidth = 2;
    gridbag.setConstraints (by_motif_panel, c);
    getContentPane ().add (by_motif_panel);

    motif_text = new JTextField ("", 18);

    c.gridwidth = GridBagConstraints.REMAINDER;
    gridbag.setConstraints (motif_text, c);
    getContentPane ().add (motif_text);

    {
      // leave a blank line to give the user a visual clue
      final JLabel blank_label = new JLabel ("");

      gridbag.setConstraints (blank_label, c);
      getContentPane ().add (blank_label);
    }

    final JPanel strand_panel = new JPanel ();

    strand_panel.setLayout (new FlowLayout (FlowLayout.LEFT));

    forward_strand_checkbox = new JCheckBox ("Forward Strand Features", true);
    strand_panel.add (forward_strand_checkbox);
    forward_strand_checkbox.addItemListener (new ItemListener () {
      public void itemStateChanged (ItemEvent _) {
        if (!forward_strand_checkbox.isSelected () &&
            !reverse_strand_checkbox.isSelected ()) {
          // make sure one of the strand is always selected
          reverse_strand_checkbox.setSelected (true);
        }
      }
    });


    reverse_strand_checkbox = new JCheckBox ("Reverse Strand Features", true);
    strand_panel.add (reverse_strand_checkbox);
    reverse_strand_checkbox.addItemListener (new ItemListener () {
      public void itemStateChanged (ItemEvent _) {
        if (!reverse_strand_checkbox.isSelected () &&
            !forward_strand_checkbox.isSelected ()) {
          // make sure one of the strand is always selected
          forward_strand_checkbox.setSelected (true);
        }
      }
    });


    c.gridwidth = GridBagConstraints.REMAINDER;
    gridbag.setConstraints (strand_panel, c);
    getContentPane ().add (strand_panel);





    final JButton select_button = new JButton ("Select");

    select_button.addActionListener (new ActionListener () {
      public void actionPerformed (ActionEvent e) {
        selection.set (getSelected ());
      }
    });

    final JButton view_button = new JButton ("View");

    view_button.addActionListener (new ActionListener () {
      public void actionPerformed (ActionEvent e) {
        final FeaturePredicate predicate =
          new FeatureFromVectorPredicate (getSelected ());

        String title = "All features";

        if (by_key_button.isSelected ()) {
          title += " with key \"" + key_selector.getSelectedItem () + "\"";
        }

        if (by_qualifier_button.isSelected ()) {
          if (qualifier_text.getText ().trim ().length () > 0) {
            title +=
              " with qualifier \"" + qualifier_selector.getSelectedItem () +
              "\" containing text \"" + qualifier_text.getText () + "\"";
          } else {
            title +=
              " with qualifier \"" +
              qualifier_selector.getSelectedItem () + "\"";
          }
        }

        if (forward_strand_checkbox.isSelected () &&
            !reverse_strand_checkbox.isSelected ()) {
          title += " on the forward strand";
        }

        if (!forward_strand_checkbox.isSelected () &&
            reverse_strand_checkbox.isSelected ()) {
          title += " on the reverse strand";
        }

        if (by_motif_button.isSelected ()) {
          title +=
            " with motif: " + motif_text.getText ().trim ().toUpperCase ();
        }
        
        if (less_than_bases_button.isSelected ()) {
          title +=
            " at most " + less_than_bases_text.getText ().trim () +
            " bases long";
        }
        
        if (greater_than_bases_button.isSelected ()) {
          title +=
            " at least " + greater_than_bases_text.getText ().trim () +
            " bases long";
        }
        
        if (less_than_exons_button.isSelected ()) {
          title +=
            " at most " + less_than_exons_text.getText ().trim () +
            " exons long";
        }
        
        if (greater_than_exons_button.isSelected ()) {
          title +=
            " at least " + greater_than_exons_text.getText ().trim () +
            " exons long";
        }
        
        final FilteredEntryGroup filtered_entry_group =
          new FilteredEntryGroup (entry_group, predicate, title);

        final FeatureListFrame feature_list_frame =
          new FeatureListFrame (title,
                                getSelection (),
                                goto_event_source, filtered_entry_group,
                                base_plot_group);

        feature_list_frame.setVisible (true);
      }
    });


    final JButton close_button = new JButton ("Close");

    close_button.addActionListener (new ActionListener () {
      public void actionPerformed (ActionEvent e) {
        Selector.this.dispose ();
      }
    });

    final FlowLayout flow_layout =
      new FlowLayout (FlowLayout.CENTER, 15, 5);

    final JPanel bottom_button_panel = new JPanel (flow_layout);

    bottom_button_panel.add (select_button);
    bottom_button_panel.add (view_button);
    bottom_button_panel.add (close_button);

    gridbag.setConstraints (bottom_button_panel, c);
    getContentPane ().add (bottom_button_panel);

    addWindowListener (new WindowAdapter () {
      public void windowClosing (WindowEvent event) {
        getEntryGroup ().removeEntryGroupChangeListener (Selector.this);
        Selector.this.dispose ();
      }
    });

    getEntryGroup ().addEntryGroupChangeListener (this);

    pack ();

    final Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
    setLocation (new Point ((screen.width - getSize ().width) / 2,
                            (screen.height - getSize ().height) / 2));

    setVisible (true);
  }

  /**
   *  Implementation of the EntryGroupChangeListener interface.  We listen to
   *  EntryGroupChange events so that we can get rid of the Navigator when the
   *  EntryGroup is no longer in use (for example when the EntryEdit is
   *  closed).
   **/
  public void entryGroupChanged (final EntryGroupChangeEvent event) {
    switch (event.getType ()) {
    case EntryGroupChangeEvent.DONE_GONE:
      getEntryGroup ().removeEntryGroupChangeListener (this);
      dispose ();
      break;
    }
  }

  /**
   *  Return those features that match the current setting of the Selector.
   **/
  private FeatureVector getSelected () {
    final FeaturePredicate key_and_qualifier_predicate;
    final FeaturePredicate motif_predicate;

    if (by_key_button.isSelected ()) {
      if (by_qualifier_button.isSelected ()) {
        final String search_text = qualifier_text.getText ().trim ();
        final String qualifier_name =
          (String) qualifier_selector.getSelectedItem ();

        if (search_text.length () == 0) {
          key_and_qualifier_predicate =
            new FeatureKeyQualifierPredicate (key_selector.getSelectedItem (),
                                              qualifier_name,
                                              true);
        } else {
          if (match_any_word_checkbox.isSelected ()) {
            final FeaturePredicateVector temp_predicates =
              new FeaturePredicateVector ();

            final StringVector words =
              StringVector.getStrings (search_text, " \t\n");

            for (int i = 0 ; i < words.size () ; ++i) {
              final String this_word = words.elementAt (i);
              final FeaturePredicate new_predicate =
                new FeatureKeyQualifierPredicate (key_selector.getSelectedItem (),
                                                  qualifier_name,
                                                  this_word,
                                                  partial_match_checkbox.isSelected (),
                                                  ignore_case_checkbox.isSelected ());

              temp_predicates.add (new_predicate);
            }

            key_and_qualifier_predicate =
              new FeaturePredicateConjunction (temp_predicates,
                                               FeaturePredicateConjunction.OR);
          } else {
            key_and_qualifier_predicate =
              new FeatureKeyQualifierPredicate (key_selector.getSelectedItem (),
                                                qualifier_name,
                                                search_text,
                                                partial_match_checkbox.isSelected (),
                                                ignore_case_checkbox.isSelected ());
            
          }
        }
      } else {
        final String search_text = qualifier_text.getText ().trim ();
        if (search_text.length () == 0) {
          key_and_qualifier_predicate =
            new FeatureKeyPredicate (key_selector.getSelectedItem ());
        } else {
          if (match_any_word_checkbox.isSelected ()) {
            final FeaturePredicateVector temp_predicates =
              new FeaturePredicateVector ();
            
            final StringVector words = 
              StringVector.getStrings (search_text, " \t\n");
            

            for (int i = 0 ; i < words.size () ; ++i) {
              final String this_word = words.elementAt (i);
              final FeaturePredicate new_predicate =
                new FeatureKeyQualifierPredicate (key_selector.getSelectedItem (),
                                                  null, // match any qialifier
                                                  this_word,
                                                  partial_match_checkbox.isSelected (),
                                                  ignore_case_checkbox.isSelected ());
              temp_predicates.add (new_predicate);
            }

            key_and_qualifier_predicate =
              new FeaturePredicateConjunction (temp_predicates,
                                               FeaturePredicateConjunction.OR);
          } else {
            key_and_qualifier_predicate =
              new FeatureKeyQualifierPredicate (key_selector.getSelectedItem (),
                                                null, // match any qialifier
                                                search_text,
                                                partial_match_checkbox.isSelected (),
                                                ignore_case_checkbox.isSelected ());
          }
        }
      }
    } else {
      key_and_qualifier_predicate = null;
    }

    if (by_motif_button.isSelected ()) {
      final AminoAcidSequence amino_acid_sequence =
        new AminoAcidSequence (motif_text.getText ().trim ());

      motif_predicate =
        new FeaturePatternPredicate (amino_acid_sequence);
    } else {
      motif_predicate = null;
    }

    FeaturePredicate less_than_bases_predicate = null;

    if (less_than_bases_button.isSelected () &&
        less_than_bases_text.getText ().trim ().length () > 0) {
      try {
        final int less_than_bases_int =
          Integer.valueOf (less_than_bases_text.getText ().trim ()).intValue ();

        less_than_bases_predicate = new FeaturePredicate () {
          public boolean testPredicate (final Feature feature) {
            if (feature.getBaseCount () <= less_than_bases_int) {
              return true;
            } else {
              return false;
            }
          }
        };
      } catch (NumberFormatException e) {
        new MessageDialog (this,
                           "warning this is not a number: " +
                           less_than_bases_text.getText ());
        less_than_bases_predicate = null;
      }
    } else {
      less_than_bases_predicate = null;
    }

    FeaturePredicate greater_than_bases_predicate = null;

    if (greater_than_bases_button.isSelected () &&
        greater_than_bases_text.getText ().trim ().length () > 0) {
      try {
        final int greater_than_bases_int =
          Integer.valueOf (greater_than_bases_text.getText ().trim ()).intValue ();

        greater_than_bases_predicate = new FeaturePredicate () {
          public boolean testPredicate (final Feature feature) {
            if (feature.getBaseCount () >= greater_than_bases_int) {
              return true;
            } else {
              return false;
            }
          }
        };
      } catch (NumberFormatException e) {
        new MessageDialog (this,
                           "warning this is not a number: " +
                           greater_than_bases_text.getText ());
        greater_than_bases_predicate = null;
      }
    } else {
      greater_than_bases_predicate = null;
    }

    FeaturePredicate less_than_exons_predicate = null;

    if (less_than_exons_button.isSelected () &&
        less_than_exons_text.getText ().trim ().length () > 0) {
      try {
        final int less_than_exons_int =
          Integer.valueOf (less_than_exons_text.getText ().trim ()).intValue ();

        less_than_exons_predicate = new FeaturePredicate () {
          public boolean testPredicate (final Feature feature) {
            if (feature.getSegments ().size () <= less_than_exons_int) {
              return true;
            } else {
              return false;
            }
          }
        };
      } catch (NumberFormatException e) {
        new MessageDialog (this,
                           "warning this is not a number: " +
                           less_than_exons_text.getText ());
        less_than_exons_predicate = null;
      }
    } else {
      less_than_exons_predicate = null;
    }

    FeaturePredicate greater_than_exons_predicate = null;

    if (greater_than_exons_button.isSelected () &&
        greater_than_exons_text.getText ().trim ().length () > 0) {
      try {
        final int greater_than_exons_int =
          Integer.valueOf (greater_than_exons_text.getText ().trim ()).intValue ();

        greater_than_exons_predicate = new FeaturePredicate () {
          public boolean testPredicate (final Feature feature) {
            if (feature.getSegments ().size () >= greater_than_exons_int) {
              return true;
            } else {
              return false;
            }
          }
        };
      } catch (NumberFormatException e) {
        new MessageDialog (this,
                           "warning this is not a number: " +
                           greater_than_exons_text.getText ());
        greater_than_exons_predicate = null;
      }
    } else {
      greater_than_exons_predicate = null;
    }

    FeaturePredicate predicate = null;

    if (!by_key_button.isSelected () && !by_motif_button.isSelected ()) {
      // default to selecting all features
      predicate = new FeaturePredicate () {
        public boolean testPredicate (final Feature feature) {
          return true;
        }
      };
    } else {
      if (motif_predicate != null && key_and_qualifier_predicate != null) {
        predicate =
          new FeaturePredicateConjunction (key_and_qualifier_predicate,
                                           motif_predicate,
                                           FeaturePredicateConjunction.AND);
      } else {
        if (motif_predicate != null) {
          predicate = motif_predicate;
        } else {
          predicate = key_and_qualifier_predicate;
        }
      }
    }

    if (less_than_bases_predicate != null) {
      predicate =
        new FeaturePredicateConjunction (predicate,
                                         less_than_bases_predicate,
                                         FeaturePredicateConjunction.AND);
    }

    if (greater_than_bases_predicate != null) {
      predicate =
        new FeaturePredicateConjunction (predicate,
                                         greater_than_bases_predicate,
                                         FeaturePredicateConjunction.AND);
    }

    if (less_than_exons_predicate != null) {
      predicate =
        new FeaturePredicateConjunction (predicate,
                                         less_than_exons_predicate,
                                         FeaturePredicateConjunction.AND);
    }

    if (greater_than_exons_predicate != null) {
      predicate =
        new FeaturePredicateConjunction (predicate,
                                         greater_than_exons_predicate,
                                         FeaturePredicateConjunction.AND);
    }

    final FeatureEnumeration test_enumerator = entry_group.features ();

    final FeatureVector return_features = new FeatureVector ();

    while (test_enumerator.hasMoreFeatures ()) {
      final Feature this_feature = test_enumerator.nextFeature ();

      if (predicate.testPredicate (this_feature)) {
        if (this_feature.isForwardFeature ()) {
          if (forward_strand_checkbox.isSelected ()) {
            return_features.add (this_feature);
          }
        } else {
          if (reverse_strand_checkbox.isSelected ()) {
            return_features.add (this_feature);
          }
        }
      }
    }

    return return_features;
  }

  /**
   *  Return the Selection object that was passed to the constructor.
   **/
  private Selection getSelection () {
    return selection;
  }

  /**
   *  Return the EntryGroup object that was passed to the constructor.
   **/
  private EntryGroup getEntryGroup () {
    return entry_group;
  }

  private JCheckBox by_key_button;
  private JCheckBox by_qualifier_button;
  private JCheckBox by_motif_button;
  private JCheckBox less_than_bases_button;
  private JCheckBox greater_than_bases_button;
  private JCheckBox less_than_exons_button;
  private JCheckBox greater_than_exons_button;
  private JCheckBox ignore_case_checkbox;
  private JCheckBox match_any_word_checkbox;
  private JCheckBox forward_strand_checkbox;
  private JCheckBox reverse_strand_checkbox;
  private KeyChoice key_selector;
  private QualifierChoice qualifier_selector;
  private JTextField qualifier_text;
  private JTextField motif_text;
  private JTextField less_than_bases_text;
  private JTextField greater_than_bases_text;
  private JTextField less_than_exons_text;
  private JTextField greater_than_exons_text;

  /**
   *  If checked the search text is allowed to match a substring of a
   *  qualifier value.
   **/
  final JCheckBox partial_match_checkbox;

  /**
   *  The EntryGroup object that was passed to the constructor.
   **/
  final EntryGroup entry_group;

  /**
   *  This is the Selection that was passed to the constructor.
   **/
  final private Selection selection;
}
