Skip to content
Snippets Groups Projects
EditMenu.java 118 KiB
Newer Older
  • Learn to ignore specific revisions
  •                ""));
           
           final FeatureEdit fe = new FeatureEdit(selection_feature, entry_group,
                                       selection, goto_event_source, edit_frame);
           
           edit_frame.addWindowListener(new WindowAdapter() 
           {
             public void windowClosing(WindowEvent event) 
             {
               fe.stopListening();
               edit_frame.dispose();
             }
           });
           
           if(cancel_listener != null)
             fe.addCancelActionListener(cancel_listener);
           if(apply_listener != null)
             fe.addApplyActionListener(apply_listener);
           edit_frame.getContentPane().add(fe);
           edit_frame.pack();
    
           Utilities.centreFrame(edit_frame);
           edit_frame.setVisible(true);
           return true;
         } 
      }
      
    
    tjc's avatar
    tjc committed
      /**
       *  Create a new EntryEdit component that contains only the selected
       *  sequence and the features in the selected range.
       **/
    
      private void editSubSequence() 
      {
        if(getSelection().isEmpty()) 
          new MessageDialog(getParentFrame(), "nothing selected");
    
        final Range range = getSelection().getSelectionRange();
        final EntryGroup new_entry_group = getEntryGroup().truncate(range);
        new EntryEdit(new_entry_group).setVisible(true);
    
    tjc's avatar
    tjc committed
      }
    
      /**
       *  Open a EntryHeaderEdit window for the default entry.
       **/
    
      private void editHeader()
      {
        final Entry default_entry = getEntryGroup().getDefaultEntry();
    
        if(default_entry == null)
        {
          final String message = "there is no default entry";
          new MessageDialog(getParentFrame(), message);
        }
        else 
        {
          if(default_entry.isReadOnly()) 
          {
            new MessageDialog(getParentFrame(),
                              "the default entry is read-only " +
                              "- cannot continue");
    
    tjc's avatar
    tjc committed
            return;
          }
    
    
          new EntryHeaderEdit(entry_group, default_entry);
    
    tjc's avatar
    tjc committed
        }
      }
    
      /**
       *  Merge the selected features into one Feature.  If there are selected
       *  segments then the owning Feature of each segment will be the Feature
       *  that is merged.  This method will create a new Feature.
       *  @param frame The JFrame to use for MessageDialog components.
       *  @param selection The Selection containing the features to merge.
       *  @param entry_group Used to get the ActionController for calling
       *    startAction() and endAction().
       **/
    
      protected static void mergeFeatures(final JFrame frame,
                                final Selection selection,
                                final EntryGroup entry_group) 
      {
        try 
        {
          entry_group.getActionController().startAction();
    
          if(!checkForSelectionFeatures(frame, selection, 10,
                     "really merge all (>10) " + "selected features?"))
    
    tjc's avatar
    tjc committed
            return;
    
    
          final FeatureVector features_to_merge = selection.getAllFeatures();
    
    tjc's avatar
    tjc committed
    
    
          if(features_to_merge.size() < 2) 
          {
            new MessageDialog(frame,
                              "nothing to merge - select more than one feature");
    
    tjc's avatar
    tjc committed
            return;
          }
    
    
          final Feature merge_feature = features_to_merge.elementAt(0);
    
    tjc's avatar
    tjc committed
    
          // make sure all the features are on the same strand
    
          for(int i = 1; i < features_to_merge.size(); ++i) 
          {
            final Feature this_feature = features_to_merge.elementAt(i);
    
            if(this_feature.isForwardFeature() !=
               merge_feature.isForwardFeature()) 
            {
              new MessageDialog(frame,
                                "all the features in a merge must be on the " +
                                "same strand");
    
    tjc's avatar
    tjc committed
              return;
            }
    
    
            if(!this_feature.getKey().equals(merge_feature.getKey())) 
            {
              new MessageDialog(frame,
                                "all the features in a merge must have the " +
                                "same key");
    
    tjc's avatar
    tjc committed
              return;
            }
          }
    
    
          if(Options.getOptions().isNoddyMode()) 
          {
    
    tjc's avatar
    tjc committed
            final YesNoDialog dialog =
    
              new YesNoDialog(frame, "Are you sure you want to merge the selected " +
                                     "features?");
            if(!dialog.getResult())
    
    tjc's avatar
    tjc committed
              return;
          }
    
    
          final Feature new_feature;
    
    tjc's avatar
    tjc committed
          //
          //  GFF merge
          if(merge_feature.getEmblFeature() instanceof GFFStreamFeature)
          {
    
            if(!merge_feature.getKey().equals(DatabaseDocument.EXONMODEL) &&
               !merge_feature.getKey().equals("pseudogenic_exon")) 
            {
              new MessageDialog(frame,"The features in a merge should be "+
                                DatabaseDocument.EXONMODEL+
                                " or pseudogenic_exon features");
              return;
            }
            
    
    tjc's avatar
    tjc committed
            gffMergeFeatures(features_to_merge, merge_feature, 
                             selection, entry_group);
    
    tjc's avatar
    tjc committed
            
            entry_group.getActionController().endAction();
    
    tjc's avatar
    tjc committed
            return;
    
    tjc's avatar
    tjc committed
            
    
          try 
          {
            new_feature = merge_feature.duplicate();
          }
          catch(ReadOnlyException e) 
          {
    
    tjc's avatar
    tjc committed
            final String message =
              "one or more of the features is read-only or is in a " +
              "read-only entry - cannot continue";
    
            new MessageDialog(frame, message);
    
    tjc's avatar
    tjc committed
            return;
          }
    
    
          for(int i = 1; i < features_to_merge.size(); ++i) 
          {
            final Feature this_feature = features_to_merge.elementAt(i);
            final QualifierVector qualifiers = this_feature.getQualifiers();
    
    tjc's avatar
    tjc committed
    
    
            for(int j = 0; j < qualifiers.size(); ++j)
            {
    
    tjc's avatar
    tjc committed
              final Qualifier this_qualifier = (Qualifier)qualifiers.elementAt(j);
    
    tjc's avatar
    tjc committed
    
    
              try 
              {
                new_feature.addQualifierValues(this_qualifier);
              }
              catch(EntryInformationException e) 
              {
                try 
                {
                  new_feature.removeFromEntry();
                } 
                catch(ReadOnlyException _) 
                {
    
    tjc's avatar
    tjc committed
                  // give up ...
                }
                final String message =
                  "destination entry does not support all the qualifiers " +
    
                  "needed by " + this_feature.getIDString();
                new MessageDialog(frame, message);
              } 
              catch(ReadOnlyException e) 
              {
    
    tjc's avatar
    tjc committed
                final String message = "the new feature is read-only so " +
                  "some qualifiers have been lost";
    
                new MessageDialog(frame, message);
    
    tjc's avatar
    tjc committed
              }
            }
    
    
            final FeatureSegmentVector segments = this_feature.getSegments();
    
    tjc's avatar
    tjc committed
    
    
            for(int j = 0; j < segments.size(); ++j)
            {
    
    tjc's avatar
    tjc committed
              final FeatureSegment this_segment =
    
    tjc's avatar
    tjc committed
    
    
              final Range this_range = this_segment.getRawRange();
    
    tjc's avatar
    tjc committed
    
    
              try 
              {
                new_feature.addSegment(this_range);
              }
              catch(ReadOnlyException e) 
              {
    
    tjc's avatar
    tjc committed
                final String message =
                  "merging failed because the entry is read-only";
    
                new MessageDialog(frame, message);
                try 
                {
                  new_feature.removeFromEntry();
    
    tjc's avatar
    tjc committed
                }
    
                catch(ReadOnlyException _) {}
    
    tjc's avatar
    tjc committed
              }
            }
          }
    
          // this is set to true when a merge is done in the next (inner) loop
          boolean keep_looping = true;
    
          // this is a bit inefficient, but there aren't normally many segments
      LOOP:
    
    tjc's avatar
    tjc committed
            final FeatureSegmentVector feature_segments =
    
    tjc's avatar
    tjc committed
    
            keep_looping = false;
    
            // now merge overlapping ranges
    
            for(int i = 0; i < feature_segments.size() - 1; ++i)
            {
    
    tjc's avatar
    tjc committed
              final FeatureSegment this_segment =
    
                                             feature_segments.elementAt(i);
              final MarkerRange this_range = this_segment.getMarkerRange();
    
    tjc's avatar
    tjc committed
    
              final FeatureSegment next_segment =
    
                                         feature_segments.elementAt(i + 1);
              final MarkerRange next_range = next_segment.getMarkerRange();
    
    tjc's avatar
    tjc committed
    
              // if it overlaps the next Range then merge it
    
              if(this_range.overlaps(next_range) &&
                  this_segment.getFrameID() == next_segment.getFrameID()) 
              {
                try 
                {
    
    tjc's avatar
    tjc committed
                  final Range new_range =
    
                    this_range.combineRanges(next_range, false).getRawRange();
                  new_feature.addSegment(new_range);
                  new_feature.removeSegment(this_segment);
                  new_feature.removeSegment(next_segment);
    
    tjc's avatar
    tjc committed
    
                  // start again
                  keep_looping = true;
                  continue LOOP;
    
    tjc's avatar
    tjc committed
                  final String message =
                    "merging failed because the entry is read-only";
    
                  new MessageDialog(frame, message);
                } 
                catch(LastSegmentException e) 
                {
                  throw new Error("internal error - tried to remove " +
                                  "last segment: " + e);
    
    tjc's avatar
    tjc committed
                }
              }
            }
          }
    
          boolean delete_old_features;
    
    
          if(Options.getOptions().isNoddyMode()) 
          {
    
    tjc's avatar
    tjc committed
            final YesNoDialog delete_old_dialog =
    
              new YesNoDialog(frame, "delete old features?");
    
    tjc's avatar
    tjc committed
    
    
            delete_old_features = delete_old_dialog.getResult();
          } 
          else
    
    tjc's avatar
    tjc committed
            delete_old_features = true;
    
    
          if(delete_old_features) 
          {
            if(getReadOnlyFeatures(features_to_merge).size() > 0)
              new MessageDialog(frame, "deletion failed because the features " +
                                "are read-only");
            else
            {
              for(int i = 0; i < features_to_merge.size(); ++i) 
              {
                try 
                {
                  features_to_merge.elementAt(i).removeFromEntry();
                }
                catch(ReadOnlyException e) 
                {
                  new MessageDialog(frame, "deletion failed one or more of the " +
                                    "features are read-only");
    
    tjc's avatar
    tjc committed
                }
              }
            }
          }
    
          selection.set(new_feature);
        } 
        finally 
        {
          entry_group.getActionController().endAction();
    
    tjc's avatar
    tjc committed
        }
      }
    
    tjc's avatar
    tjc committed
      
      /**
       * Merge features / gene model - creating gene model if not already present
       * @param features_to_merge
       * @param merge_feature
       * @param selection
       * @param entry_group
       */
    
    tjc's avatar
    tjc committed
      public static void gffMergeFeatures(final FeatureVector features_to_merge,
    
    tjc's avatar
    tjc committed
                                    final Feature merge_feature,
                                    final Selection selection,
                                    final EntryGroup entry_group)
      {
        try
        {
    
    tjc's avatar
    tjc committed
          Qualifier parentQualifier;
    
    tjc's avatar
    tjc committed
          ChadoCanonicalGene chadoGene = null;
    
    tjc's avatar
    tjc committed
          ChadoCanonicalGene chadoGene2 = null;
    
    tjc's avatar
    tjc committed
          String transcriptId = null;
    
    tjc's avatar
    tjc committed
          java.util.List geneModels = getGeneModels(features_to_merge);
          
          if(geneModels.size() == 0)
    
    tjc's avatar
    tjc committed
          {
            // create gene model
    
    tjc's avatar
    tjc committed
            final Location geneLocation = new Location(selection.getSelectionRange());
    
    tjc's avatar
    tjc committed
           
    
    tjc's avatar
    tjc committed
            final String parentId = GeneUtils.getUniqueName(
                     merge_feature.getEmblFeature())+":gene";
            final QualifierVector qualifiers = new QualifierVector();
    
    tjc's avatar
    tjc committed
            qualifiers.setQualifier(new Qualifier("ID", parentId));
            
            // create gene
    
            final Key key;
            if(merge_feature.getKey().getKeyString().equals("pseudogenic_exon"))
              key = new Key("pseudogene");
            else
              key = new Key("gene");
            
    
    tjc's avatar
    tjc committed
            Feature parentGene = merge_feature.getEntry().createFeature(key, 
    
    tjc's avatar
    tjc committed
                                                     geneLocation, qualifiers);
            
            chadoGene = new ChadoCanonicalGene();
            chadoGene.setGene(parentGene.getEmblFeature());
            ((uk.ac.sanger.artemis.io.GFFStreamFeature)
                (parentGene.getEmblFeature())).setChadoGene(chadoGene);
            
            // create transcript
            Feature transcript = GeneViewerPanel.createTranscript(chadoGene, entry_group);
    
            ((uk.ac.sanger.artemis.io.GFFStreamFeature)
                (transcript.getEmblFeature())).setChadoGene(chadoGene);
    
    tjc's avatar
    tjc committed
            transcriptId = GeneUtils.getUniqueName(transcript.getEmblFeature());
    
    tjc's avatar
    tjc committed
            
            parentQualifier = new Qualifier("Parent", transcriptId);
            merge_feature.setQualifier(parentQualifier);
          }
          else
    
    tjc's avatar
    tjc committed
          { 
            boolean isMultipleTranscript = false;
            for(int i=0; i<geneModels.size(); i++)
            {
              if(((ChadoCanonicalGene)geneModels.get(i)).getTranscripts().size() != 1)
                isMultipleTranscript = true;
            }
    
    tjc's avatar
    tjc committed
            
    
    tjc's avatar
    tjc committed
            if(features_to_merge.size() > 2 || 
               geneModels.size() > 2 ||
               isMultipleTranscript)
            {
              JOptionPane.showMessageDialog(null,
                  "This option cannot be used to merge more than 2 gene models.\n"+
                  "Select two exons in two gene models to be merged.\n"+
                  "The gene models must have just one transcript.");
              return;
            }
            
            logger4j.debug("Found "+geneModels.size()+" gene models for merging");
            
            if(geneModels.size() == 2)
            {
              parentQualifier = merge_feature.getQualifierByName("Parent");
              transcriptId = (String)parentQualifier.getValues().get(0);
              chadoGene = ((GFFStreamFeature)merge_feature.getEmblFeature()).getChadoGene();
              
              final String chadoGeneName = chadoGene.getGeneUniqueName();
              for(int i=0; i<geneModels.size(); i++)
              {
                final ChadoCanonicalGene thisChadoGene = (ChadoCanonicalGene)geneModels.get(i);
                if(!thisChadoGene.equals(chadoGeneName))
                  chadoGene2 = thisChadoGene;
              }
            }
            else
            {
              chadoGene = (ChadoCanonicalGene)geneModels.get(0);
              transcriptId = GeneUtils.getUniqueName(
                  (uk.ac.sanger.artemis.io.Feature)chadoGene.getTranscripts().get(0));
              parentQualifier = new Qualifier("Parent", transcriptId);
              merge_feature.setQualifier(parentQualifier);
            }
            
            // TODO - merge transcript / peptide qualifiers into chadoGene ??
    
    tjc's avatar
    tjc committed
          }
          
          final RangeVector ranges = new RangeVector();
          java.util.Hashtable id_range_store = 
                 ((GFFStreamFeature)merge_feature.getEmblFeature()).getSegmentRangeStore();
          
          for(int i=1; i< features_to_merge.size(); i++)
          {
            final Feature this_feature = features_to_merge.elementAt(i);
            final FeatureSegmentVector segments = this_feature.getSegments();
            
    
    tjc's avatar
    tjc committed
            this_feature.setQualifier(parentQualifier);
    
    tjc's avatar
    tjc committed
            
            for(int j = 0; j < segments.size(); ++j)
            {
              final FeatureSegment this_segment = segments.elementAt(j);
              ranges.add(this_segment.getRawRange());
            }         
          }
          
          for(int i=0; i<features_to_merge.size(); i++)
          {
            final Feature this_feature = features_to_merge.elementAt(i);
    
    tjc's avatar
    tjc committed
            // remove the duplicate feature
            if(i > 0)
    
    tjc's avatar
    tjc committed
              this_feature.getEntry().remove(this_feature, false);
    
    tjc's avatar
    tjc committed
          }
    
    
    tjc's avatar
    tjc committed
          // add the segments
    
    tjc's avatar
    tjc committed
          //uk.ac.sanger.artemis.chado.ChadoTransactionManager.addSegments = false;
    
    tjc's avatar
    tjc committed
          for(int i = 0; i < ranges.size(); i++)
          {
            final Range range = (Range)ranges.get(i);
    
    tjc's avatar
    tjc committed
            final String segId = chadoGene.autoGenerateSplicedFeatureName(transcriptId);
            id_range_store.put(segId,range);
            ((GFFStreamFeature)merge_feature.getEmblFeature()).setSegmentRangeStore(id_range_store);
            
    
    tjc's avatar
    tjc committed
            merge_feature.addSegment(range);
          }
    
    tjc's avatar
    tjc committed
          //uk.ac.sanger.artemis.chado.ChadoTransactionManager.addSegments = true;
    
    tjc's avatar
    tjc committed
          
          // set the new ID for the joined feature
    
          final String ID = ((GFFStreamFeature)merge_feature.getEmblFeature()).getSegmentID(
    
    tjc's avatar
    tjc committed
                                             merge_feature.getLocation().getRanges());
    
          final Qualifier qualifier = new Qualifier("ID", ID);
    
    tjc's avatar
    tjc committed
          merge_feature.getEmblFeature().setQualifier(qualifier);
          chadoGene.addSplicedFeatures(transcriptId, merge_feature.getEmblFeature(), true);
    
          ((GFFStreamFeature)merge_feature.getEmblFeature()).setChadoGene(chadoGene);
    
    tjc's avatar
    tjc committed
          
          if(chadoGene2 != null)
          {
            logger4j.debug("Now DELETE "+chadoGene2.getGeneUniqueName());
            GeneUtils.deleteAllFeature((Feature)chadoGene2.getGene().getUserData(), chadoGene2);
          }
          if(chadoGene != null)
          {
            logger4j.debug("Check gene boundaries of: "+chadoGene.getGeneUniqueName());
            GeneUtils.checkGeneBoundary(chadoGene);
          }
    
    tjc's avatar
    tjc committed
        }
        catch(ReadOnlyException e)
        {
          final String message = "one or more of the features is read-only or is in a "
              + "read-only entry - cannot continue";
          new MessageDialog(null, message);
          return;
        }
        catch(InvalidRelationException ire){ ire.printStackTrace(); }
        catch(EntryInformationException e)
        {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
        catch(OutOfRangeException e)
        {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      }
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
      /**
       * Get the chado gene models for a list of features
       * @param features
       * @return
       */
      private static java.util.List getGeneModels(final FeatureVector features)
      {
        final java.util.List geneModels = new Vector();
        final java.util.List geneModelNames = new Vector();
        for(int i=0; i< features.size(); i++)
        {
          if(features.elementAt(i).getEmblFeature() instanceof GFFStreamFeature)
          {
            final GFFStreamFeature this_feature = 
               (GFFStreamFeature)features.elementAt(i).getEmblFeature();
          
            if(this_feature.getChadoGene() != null)
            {
              final String this_name = 
                this_feature.getChadoGene().getGeneUniqueName();
              if(!geneModelNames.contains(this_name))
              {
                geneModels.add(this_feature.getChadoGene());
                geneModelNames.add(this_name);
              }
            }
          }
        }
        return geneModels;
      }
      
    
    tjc's avatar
    tjc committed
      /**
       *  If the selection contains exactly two segments and those segments are
       *  adjacent in the same feature, split the feature into two pieces.  The
       *  orignal feature is truncated and a new feature is created.  The
       *  qualifiers of the old feature are copied to new feature.
       *  @param frame The JFrame to use for MessageDialog components.
       *  @param selection The Selection containing the segments to unmerge.
       *  @param entry_group Used to get the ActionController for calling
       *    startAction() and endAction().
       **/
    
    tjc's avatar
    tjc committed
      private static void unmergeFeature(final JFrame frame,
    
    tjc's avatar
    tjc committed
                                  final Selection selection,
    
    tjc's avatar
    tjc committed
                                  final EntryGroup entry_group) 
      {
        try 
        {
    
    tjc's avatar
    tjc committed
          entry_group.getActionController ().startAction ();
    
          final FeatureSegmentVector selected_segments =
            selection.getSelectedSegments ();
    
    
    tjc's avatar
    tjc committed
          if (selected_segments.size () != 2) 
          {
    
    tjc's avatar
    tjc committed
            final String message =
              "you need to select exactly two exons use unmerge";
    
    tjc's avatar
    tjc committed
            new MessageDialog(frame, message);
    
    tjc's avatar
    tjc committed
            return;
          }
    
          FeatureSegment first_segment = selected_segments.elementAt (0);
          FeatureSegment second_segment = selected_segments.elementAt (1);
    
    
    tjc's avatar
    tjc committed
          if(first_segment.getFeature () != second_segment.getFeature ()) 
          {
    
    tjc's avatar
    tjc committed
            final String message =
              "you need to select two exons from the same feature to use unmerge";
            new MessageDialog (frame, message);
            return;
          }
    
          final Feature segment_feature = first_segment.getFeature ();
    
          final FeatureSegmentVector all_feature_segments =
            segment_feature.getSegments ();
    
          int index_of_first_segment =
            all_feature_segments.indexOf (first_segment);
          int index_of_second_segment =
            all_feature_segments.indexOf (second_segment);
    
    
    tjc's avatar
    tjc committed
          if(index_of_first_segment - index_of_second_segment < -1 ||
             index_of_first_segment - index_of_second_segment > 1) 
          {
    
    tjc's avatar
    tjc committed
            final String message =
              "you need to select two adjacent exons to use unmerge";
            new MessageDialog (frame, message);
            return;
          }
    
    
    tjc's avatar
    tjc committed
          if(index_of_second_segment <  index_of_first_segment) 
          {
    
    tjc's avatar
    tjc committed
            // swap the segments for consistency
            final FeatureSegment temp_segment = first_segment;
            final int temp_segment_index = index_of_first_segment;
    
            first_segment = second_segment;
            index_of_first_segment = index_of_second_segment;
    
            second_segment = temp_segment;
            index_of_second_segment = temp_segment_index;
          }
    
    
    tjc's avatar
    tjc committed
          try 
          {
    
    tjc's avatar
    tjc committed
            final Feature new_feature;
            if(segment_feature.getEmblFeature() instanceof GFFStreamFeature)
            {
              final FeatureVector chadoGenes = new FeatureVector();
              chadoGenes.add(segment_feature);
              final Vector duplicateGenes = duplicateGeneFeatures(frame, chadoGenes, entry_group);
              
              // get the new duplicate spliced feature
              ChadoCanonicalGene chado_gene = (ChadoCanonicalGene)duplicateGenes.get(0);
              // assumes single transcript
              uk.ac.sanger.artemis.io.Feature transcript = 
                (uk.ac.sanger.artemis.io.Feature)chado_gene.getTranscripts().get(0);
              // get the spliced feature with the same key as the segment
              // selected to unmerge
              uk.ac.sanger.artemis.io.Feature spliced = (uk.ac.sanger.artemis.io.Feature)
                chado_gene.getSpliceSitesOfTranscript(GeneUtils.getUniqueName(transcript), 
                  first_segment.getFeature().getKey().getKeyString()).get(0);
              new_feature = (Feature)spliced.getUserData();
            }
            else
              new_feature = segment_feature.duplicate(true);
    
    tjc's avatar
    tjc committed
            // we set the Selection later
            selection.clear ();
    
            // delete the segments starting at index_of_second_segment from
            // segment_feature and delete the segments up to (and including)
            // index_of_first_segment from new_feature
    
    
    tjc's avatar
    tjc committed
            for(int i = all_feature_segments.size() - 1;
                i >= index_of_second_segment; --i)
            {
    
    tjc's avatar
    tjc committed
              segment_feature.getSegments ().elementAt (i).removeFromFeature ();
            }
    
            // remove the first segment of new_feature index_of_first_segment times
    
    tjc's avatar
    tjc committed
            for (int i = 0; i <= index_of_first_segment; ++i) 
    
    tjc's avatar
    tjc committed
              new_feature.getSegments ().elementAt (0).removeFromFeature ();
    
    tjc's avatar
    tjc committed
            if(segment_feature.getEmblFeature() instanceof GFFStreamFeature)
            {
    
    tjc's avatar
    tjc committed
              GeneUtils.checkGeneBoundary(
                  ((GFFStreamFeature)segment_feature.getEmblFeature()).getChadoGene());
              GeneUtils.checkGeneBoundary(
                  ((GFFStreamFeature)new_feature.getEmblFeature()).getChadoGene());
    
    tjc's avatar
    tjc committed
            }
            selection.set (segment_feature.getSegments ().lastElement ());
            selection.add (new_feature.getSegments ().elementAt (0));
    
    tjc's avatar
    tjc committed
          } 
          catch (ReadOnlyException e) 
          {
    
    tjc's avatar
    tjc committed
            final String message =
              "the selected exons (in " +
              segment_feature.getIDString () +
              ") are in a read only entry - cannot continue";
            new MessageDialog (frame, message);
    
    tjc's avatar
    tjc committed
          } 
          catch (LastSegmentException e) 
          {
    
    tjc's avatar
    tjc committed
            throw new Error ("internal error - unexpected exception: " + e);
          }
    
    tjc's avatar
    tjc committed
        } 
        finally 
        {
    
    tjc's avatar
    tjc committed
          entry_group.getActionController ().endAction ();
        }
      }
    
    
    tjc's avatar
    tjc committed
      /**
       *  If the selection contains exactly one feature this routine will
       *  remove all the joins.
       *  @param frame The JFrame to use for MessageDialog components.
       *  @param selection The Selection containing the segments to unmerge.
       *  @param entry_group Used to get the ActionController for calling
       *    startAction() and endAction().
       **/
    
    tjc's avatar
    tjc committed
      private static void unmergeAllFeature(final JFrame frame,
    
    tjc's avatar
    tjc committed
                                     final Selection selection,
                                     final EntryGroup entry_group) 
      {
        try 
        {
          entry_group.getActionController ().startAction ();
    
          final FeatureVector delete_features = selection.getAllFeatures();
          if(delete_features.size() > 1)
          {
            new MessageDialog (frame, "Select just one feature");             
            return;
          }
    
          final FeatureSegmentVector selected_segments =
                       delete_features.elementAt(0).getSegments();
          try  
          {
            Vector new_features = new Vector();
            Vector segment_to_remove = new Vector();
    
            FeatureSegment[] selected_segments_array = new FeatureSegment[selected_segments.size()];
            for(int i=0; i<selected_segments.size(); i++)
            {
              FeatureSegment seg   = selected_segments.elementAt(i);
              int index_of_segment = selected_segments.indexOf(seg); 
              selected_segments_array[index_of_segment] = seg;
            }
            
            for(int i=0; i<selected_segments.size()-1; i++)
            {
    
    tjc's avatar
    tjc committed
              FeatureSegment segment  = selected_segments_array[i];
    
    tjc's avatar
    tjc committed
              Feature segment_feature = segment.getFeature();
    
    tjc's avatar
    tjc committed
              final Feature new_feature = segment_feature.duplicate();
    
    tjc's avatar
    tjc committed
              segment_to_remove.add(segment);
    
              FeatureSegmentVector new_segments = new_feature.getSegments();
    
              Vector removals = new Vector();
              for(int j = 0 ; j <new_segments.size(); j++)
              {
                if(i != j)
    
    tjc's avatar
    tjc committed
                  removals.add(new_segments.elementAt(j));
    
    tjc's avatar
    tjc committed
              }
    
              for(int j = 0; j < removals.size(); j++)
                new_feature.removeSegment( (FeatureSegment)removals.get(j) );  
    
              new_features.add(new_feature);
            }
    
    
    tjc's avatar
    tjc committed
            final int size = segment_to_remove.size();
            for(int i=0; i<size; i++)
    
    tjc's avatar
    tjc committed
            {
    
    tjc's avatar
    tjc committed
              selected_segments_array[size-1].getFeature().removeSegment(
                               (FeatureSegment)segment_to_remove.get(i) );
    
    tjc's avatar
    tjc committed
            Feature feature;
    
    tjc's avatar
    tjc committed
            for(int i=0; i<new_features.size(); i++)
    
    tjc's avatar
    tjc committed
            {
              feature = (Feature)new_features.get(i);
              selection.add(feature.getSegments().elementAt(0));
              
              // set GFF ID's
              if(feature.getEmblFeature() instanceof GFFStreamFeature)
                setGffId(selected_segments_array[0].getFeature(), feature);
            }
            
            // set GFF ID's
            feature = selected_segments_array[size-1].getFeature();
            if(feature.getEmblFeature() instanceof GFFStreamFeature)
              setGffId(selected_segments_array[0].getFeature(), feature);
    
    tjc's avatar
    tjc committed
          } 
          catch(ReadOnlyException e)
          {
            final String message =
              "the selected exons (in " +
              delete_features.elementAt(0).getIDString () +
              ") are in a read only entry - cannot continue";
            new MessageDialog (frame, message);
          }
          catch (LastSegmentException e) 
          {
            throw new Error ("internal error - unexpected exception: " + e);
          }
        } 
        finally 
        {
          entry_group.getActionController ().endAction ();
        }
      }
    
    
    tjc's avatar
    tjc committed
      /**
       * Sets the ID based on the new features ranges.
       * @param feature_original
       * @param feature_new
       */
      private static void setGffId(final Feature feature_original,
                            final Feature feature_new)
      {
        RangeVector ranges = feature_new.getLocation().getRanges();
        GFFStreamFeature gff_feature = 
           (GFFStreamFeature)feature_original.getEmblFeature();
        String id1 = gff_feature.getSegmentID(ranges);
        
        try
        {
          feature_new.getEmblFeature().setQualifier(new Qualifier("ID", id1));
        }
        catch(ReadOnlyException e)
        {
          e.printStackTrace();
        }
        catch(EntryInformationException e)
        {
          e.printStackTrace();
        }
        
      }
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
      /**
       *  Create a QualifierEditor JFrame that acts on the selected features.
       *  @param frame The JFrame to use for MessageDialog components.
       **/
      private void addQualifiers (final JFrame frame, final Selection selection) {
        if (!checkForSelectionFeatures (frame, selection)) {
          return;
        }
    
        final FeatureVector selected_features = selection.getAllFeatures ();
    
        if (getReadOnlyFeatures (selected_features).size () > 0) {
          new MessageDialog (frame,
                             "one or more of the selected features is read-only " +
                             "- cannot continue");
          return;
        }
    
        final QualifierEditor qualifier_editor =
          new QualifierEditor (selected_features, getEntryGroup ());
    
        qualifier_editor.setVisible (true);
      }
    
      /**
       *  Offer the user a choice of qualifier to remove from the selected features
       **/
      private void removeQualifier (final JFrame frame, final Selection selection) {
        if (!checkForSelectionFeatures (frame, selection)) {
          return;
        }
    
        final FeatureVector selected_features = selection.getAllFeatures ();
    
        final StringVector qualifier_names =
          Feature.getAllQualifierNames (selected_features);
    
        if (qualifier_names.size () == 0) {
          new MessageDialog (getParentFrame (), "feature has no qualifiers");
          return;
        }
    
        final ChoiceFrame choice_frame =
          new ChoiceFrame ("Select a qualifer name", qualifier_names);
    
        final JComboBox choice = choice_frame.getChoice ();
    
    
        final int MAX_VISIBLE_ROWS = 30;
        
        choice.setMaximumRowCount (MAX_VISIBLE_ROWS);
        
        choice.addItemListener (new ItemListener () {
          public void itemStateChanged (ItemEvent _) {
            removeQualifierFromFeatures (selected_features,
                                         (String) choice.getSelectedItem ());
            choice_frame.setVisible (false);
            choice_frame.dispose ();
          }
        });
    
        choice_frame.getOKButton ().addActionListener (new ActionListener () {
          public void actionPerformed (ActionEvent _) {
            removeQualifierFromFeatures (selected_features,
                                         (String) choice.getSelectedItem ());
          }
        });
    
        choice_frame.setVisible (true);
      }
    
    tjc's avatar
    tjc committed
      private void convertKeys(final JFrame frame, 
                               final Selection selection)
      {
        if (!checkForSelectionFeatures (frame, selection)) 
          return;
    
        final FeatureVector selected_features = selection.getAllFeatures ();
        
        Entry default_entry = getEntryGroup().getDefaultEntry();
        if(default_entry == null)
          default_entry = getEntryGroup().elementAt(0);
      
        final EntryInformation default_entry_information =
                            default_entry.getEntryInformation();
    
        final KeyChoice key_selector = new KeyChoice(default_entry_information);
        final String options[] = { "Convert", "Cancel" };
        
        final int opt = JOptionPane.showOptionDialog(frame, 
            key_selector, "Convert Key(s) of Selected Features", 
            JOptionPane.OK_CANCEL_OPTION,
            JOptionPane.QUESTION_MESSAGE, null, options, options[0]);
    
        if(opt == 1)
          return;
        
        entry_group.getActionController ().startAction ();
        try
        {
          for(int i=0; i<selected_features.size(); i++)
          {
            Feature feature = selected_features.elementAt(i);
            feature.set(key_selector.getSelectedItem(), 
                  feature.getLocation(), feature.getQualifiers());
          }
        }
        catch(ReadOnlyException e)
        {
          JOptionPane.showMessageDialog(frame, 
              "Cannot convert read-only features.", 
              "Error Converting Key(s)", JOptionPane.ERROR_MESSAGE);
        }
        catch(EntryInformationException e)
        {
          JOptionPane.showMessageDialog(frame, 
              e.getMessage(), 
              "Error Converting Key(s)", JOptionPane.ERROR_MESSAGE);
        }
        catch(OutOfRangeException e)
        {
          JOptionPane.showMessageDialog(frame, 
              e.getMessage(), 
              "Error Converting Key(s)", JOptionPane.ERROR_MESSAGE);
        }
        finally
        {
          entry_group.getActionController ().endAction();
        }
      }
      
    
    tjc's avatar
    tjc committed
      /**
       * Offer the user a choice of qualifier to convert the name of
       * from the selected features
       * @param frame
       * @param selection
       */
      private void convertQualifier (final JFrame frame, 
                                     final Selection selection) 
      {
        if (!checkForSelectionFeatures (frame, selection)) 
          return;
    
        final FeatureVector selected_features = selection.getAllFeatures ();
        final StringVector qualifier_names =
          Feature.getAllQualifierNames (selected_features);
    
        if(qualifier_names.size () == 0) 
        {
          new MessageDialog (getParentFrame (), "feature has no qualifiers");
          return;
        }
    
        Box yBox = Box.createVerticalBox();
        final JComboBox convertFrom = new JComboBox(qualifier_names);
        final QualifierChoice convertTo = new QualifierChoice(
            getEntryGroup().getDefaultEntry().getEntryInformation(),
            selected_features.elementAt(0).getKey(),null,
            false);
        
        Box xBox = Box.createHorizontalBox();
        xBox.add(new JLabel("Convert all qualifiers of type:"));
        xBox.add(Box.createHorizontalGlue());
        yBox.add(xBox);
        yBox.add(convertFrom);
        xBox = Box.createHorizontalBox();
        xBox.add(new JLabel("To:"));
        xBox.add(Box.createHorizontalGlue());
        yBox.add(xBox);
        yBox.add(convertTo);
        
        int select = JOptionPane.showConfirmDialog(frame, yBox, 
            "Convert Qualifiers", 
            JOptionPane.OK_CANCEL_OPTION, 
            JOptionPane.QUESTION_MESSAGE);
        
        String oldQualifierName = (String)convertFrom.getSelectedItem();
        String newQualifierName = (String)convertTo.getSelectedItem();
        
        if(select == JOptionPane.CANCEL_OPTION ||
            oldQualifierName.equals(newQualifierName))
          return;
        
        try
        {
          for(int i=0; i<selected_features.size(); i++)
          {
            Feature feature = selected_features.elementAt(i);
            QualifierVector qualifiers = feature.getQualifiers();
            int index = qualifiers.indexOfQualifierWithName(oldQualifierName);
            if(index == -1)
              continue;