Skip to content
Snippets Groups Projects
GeneUtils.java 38.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • tjc's avatar
    tjc committed
    /* GeneUtils.java
     *
     * This file is part of Artemis
     * Copyright (C) 2007  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.
     *
     **/
    package uk.ac.sanger.artemis.components.genebuilder;
    
    import java.awt.BorderLayout;
    
    tjc's avatar
    tjc committed
    import java.awt.Cursor;
    
    tjc's avatar
    tjc committed
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    tjc's avatar
    tjc committed
    import java.util.Collection;
    
    tjc's avatar
    tjc committed
    import java.util.Enumeration;
    import java.util.Hashtable;
    
    tjc's avatar
    tjc committed
    import java.util.Iterator;
    
    tjc's avatar
    tjc committed
    import java.util.List;
    
    tjc's avatar
    tjc committed
    import java.util.Set;
    
    tjc's avatar
    tjc committed
    import java.util.Vector;
    
    import javax.swing.Box;
    import javax.swing.DefaultListModel;
    import javax.swing.JButton;
    
    tjc's avatar
    tjc committed
    import javax.swing.JCheckBox;
    
    tjc's avatar
    tjc committed
    import javax.swing.JFrame;
    
    tjc's avatar
    tjc committed
    import javax.swing.JLabel;
    import javax.swing.JList;
    import javax.swing.JOptionPane;
    import javax.swing.JPanel;
    import javax.swing.JScrollPane;
    
    
    tjc's avatar
    tjc committed
    import org.gmod.schema.sequence.FeatureCvTerm;
    import org.gmod.schema.sequence.FeatureDbXRef;
    import org.gmod.schema.sequence.FeaturePub;
    import org.gmod.schema.sequence.FeatureSynonym;
    
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.chado.ClusterLazyQualifierValue;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.chado.FeatureLocLazyQualifierValue;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.components.EditMenu;
    import uk.ac.sanger.artemis.components.MessageDialog;
    import uk.ac.sanger.artemis.components.SelectionMenu;
    import uk.ac.sanger.artemis.io.ChadoCanonicalGene;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.io.DatabaseDocumentEntry;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.io.DocumentEntry;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.io.EntryInformationException;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.io.Feature;
    import uk.ac.sanger.artemis.io.GFFStreamFeature;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.io.InvalidRelationException;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.io.Key;
    
    import uk.ac.sanger.artemis.io.KeyVector;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.io.Location;
    import uk.ac.sanger.artemis.io.Qualifier;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.io.QualifierLazyLoading;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.io.QualifierVector;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.io.Range;
    import uk.ac.sanger.artemis.io.RangeVector;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.sequence.MarkerRange;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.util.ByteBuffer;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.util.DatabaseDocument;
    import uk.ac.sanger.artemis.util.OutOfRangeException;
    import uk.ac.sanger.artemis.util.ReadOnlyException;
    import uk.ac.sanger.artemis.Entry;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.EntryGroup;
    import uk.ac.sanger.artemis.EntryVector;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.FeatureKeyQualifierPredicate;
    import uk.ac.sanger.artemis.FeaturePredicate;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.FeatureVector;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.GotoEventSource;
    import uk.ac.sanger.artemis.Selection;
    
    tjc's avatar
    tjc committed
    
    
    public class GeneUtils
    {
      private static final long serialVersionUID = 1L;
      private static Vector hideFeatures = new Vector();
    
    tjc's avatar
    tjc committed
      private static JCheckBox showObsolete = new JCheckBox("Show Obsolete Features",false);
    
    tjc's avatar
    tjc committed
      
      static
      {
        hideFeatures.add("polypeptide");
    
    tjc's avatar
    tjc committed
        hideFeatures.add(DatabaseDocument.TRANSCRIPT);
    
    tjc's avatar
    tjc committed
        hideFeatures.add("pseudogenic_transcript");
      }
      
    
    tjc's avatar
    tjc committed
      /**
       * Used when a whole sequence is loaded in and the features are loaded
       * lazily
       * @param feature
       */
      public static void addLazyQualifiers(final GFFStreamFeature feature)
      {
        if(feature.isLazyLoaded() || feature.getChadoLazyFeature() == null)
          return;
        
        // synonyms
        final Collection featureSynonyms = feature.getChadoLazyFeature().getFeatureSynonyms();
        
        final Iterator it = featureSynonyms.iterator();
        while(it.hasNext())
        {
          final FeatureSynonym featureSynonym = (FeatureSynonym) it.next();
          final String name = featureSynonym.getSynonym().getCvTerm().getName();
          String value = featureSynonym.getSynonym().getName();
          
          if(!featureSynonym.isCurrent())
            value.concat(GFFStreamFeature.encode(";current=false"));      
          
          Qualifier qualifier = feature.getQualifiers().getQualifierByName(name);
          if(qualifier == null)
            qualifier = new Qualifier(name, value);
          else
            qualifier.addValue(value);
          
          feature.getQualifiers().setQualifier(qualifier);
        }
        
        // dbxrefs
        final Collection featureDbXRefs = feature.getChadoLazyFeature().getFeatureDbXRefs();
        final Iterator it2 = featureDbXRefs.iterator();
        while(it2.hasNext())
        {
          final FeatureDbXRef featureDbXRef = (FeatureDbXRef) it2.next();
          String value = featureDbXRef.getDbXRef().getDb().getName() + ":" + 
                         featureDbXRef.getDbXRef().getAccession();
          
          Qualifier qualifier = feature.getQualifiers().getQualifierByName("Dbxref");
          if(qualifier == null)
            qualifier = new Qualifier("Dbxref", value);
          else
            qualifier.addValue(value);
          feature.getQualifiers().setQualifier(qualifier);
        }
        
        
        // feature cvterms (GO, product....)
        final Collection featureCvTerms = feature.getChadoLazyFeature().getFeatureCvTerms();
        final Iterator it3 = featureCvTerms.iterator();
        
        while(it3.hasNext())
        {
          FeatureCvTerm featureCvTerm = (FeatureCvTerm)it3.next();
          List featureCvTermDbXRefList = null;
          
          if(featureCvTerm.getFeatureCvTermDbXRefs() != null)
            featureCvTermDbXRefList = new Vector(featureCvTerm.getFeatureCvTermDbXRefs());
          
          List featureCvTermPubList = null;
          
          if(featureCvTerm.getFeatureCvTermPubs() != null)
            featureCvTermPubList = new Vector(featureCvTerm.getFeatureCvTermPubs());
           
          ByteBuffer this_buff = new ByteBuffer();
          DatabaseDocument.appendControlledVocabulary(this_buff, null, featureCvTerm,
                                     featureCvTermDbXRefList,featureCvTermPubList, null, false);
          
          final String qualifierString = new String(this_buff.getBytes());
          int ind = qualifierString.indexOf('=');
          final String name  = qualifierString.substring(0, ind);
          final String value = GFFStreamFeature.decode(
              qualifierString.substring(ind+1, qualifierString.length()-1));
          
          Qualifier qualifier = feature.getQualifiers().getQualifierByName(name);
          if(qualifier == null)
            qualifier = new Qualifier(name, value);
          else
            qualifier.addValue(value);
          feature.getQualifiers().setQualifier(qualifier);
        }
        
        // feature pubs - literature
        final Collection featurePubs = feature.getChadoLazyFeature().getFeaturePubs();
        final Iterator it4 = featurePubs.iterator();
        while(it4.hasNext())
        {
          FeaturePub featurePub = (FeaturePub)it4.next();
          
          Qualifier qualifier = feature.getQualifiers().getQualifierByName("literature");
          if(qualifier == null)
            qualifier = new Qualifier("literature", featurePub.getPub().getUniqueName());
          else
            qualifier.addValue(featurePub.getPub().getUniqueName());
          feature.getQualifiers().setQualifier(qualifier);
        }
        
        feature.setLazyLoaded(true);
      }
      
    
    tjc's avatar
    tjc committed
      /**
       * Used to reverse complement all the gene model features
       * @param chadoGene
       */
      public static void complementGeneModel(final ChadoCanonicalGene chadoGene)
      {
        if(chadoGene == null)
          return;
        try
        {
          final Feature gene = chadoGene.getGene();
          final boolean complement = gene.getLocation().isComplement();
          gene.setLocation(gene.getLocation().getComplement());
          final Set kids = chadoGene.getChildren(gene);
          final Iterator it = kids.iterator();
          while(it.hasNext())
          {
            final Feature f = (Feature)it.next();
            final RangeVector rv = f.getLocation().getRanges();
            rv.reverse();
            f.setLocation(new Location(rv, !complement));
          }
        }
        catch(ReadOnlyException e)
        {
          e.printStackTrace();
        }
        catch(OutOfRangeException e)
        {
          e.printStackTrace();
        }
      }
      
    
    tjc's avatar
    tjc committed
      public static void addSegment(final GFFStreamFeature feature,
    
                                    final RangeVector rangesToAdd,
    
    tjc's avatar
    tjc committed
                                    final String transcriptName) 
             throws ReadOnlyException, EntryInformationException
      {
        // add new ID
        final Hashtable id_store = feature.getSegmentRangeStore();
        String prefix[] = null;
        Enumeration enum_ids = id_store.keys();
        while(enum_ids.hasMoreElements())
        {
          String id = (String) enum_ids.nextElement();
          prefix = feature.getPrefix(id, ':');
          if(prefix[0] != null)
            break;
        }
    
        // USE PREFIX TO CREATE NEW ID
    
        RangeVector rv = (RangeVector)feature.getLocation().getRanges().clone();
        for(int i=0; i<rangesToAdd.size(); i++)
    
    tjc's avatar
    tjc committed
        {
    
          final Range range = (Range) rangesToAdd.elementAt(i);
          final String ID;
          if(prefix[0] != null)
          {
            int auto_num = feature.getAutoNumber(prefix[0], ':');
            ID = prefix[0] + ":" + auto_num;
            feature.getSegmentRangeStore().put(ID, range);
          }
          else
          {
            String key = feature.getKey().toString();
            ID = transcriptName + ":" + key + ":1";
            feature.getSegmentRangeStore().put(ID, range);
          }
    
    tjc's avatar
    tjc committed
    
    
          if(!rv.containsRange(range))
            rv.add(range);
        }
    
    tjc's avatar
    tjc committed
        
        feature.setQualifier(new Qualifier("ID", feature.getSegmentID( rv )));
      }
      
    
    tjc's avatar
    tjc committed
      /**
       * Used when writing the database entry to a file. This routine
       * forces lazy-loading qualifier values to be read in full.
       * @param entry
       * @param parent
       */
    
    tjc's avatar
    tjc committed
      public static void lazyLoadAll(final Entry entry, final JFrame parent)
      {
        final List lazySimilarityValues = new Vector();
        final List lazyClusterValues = new Vector();
        final FeatureVector features = entry.getAllFeatures();
        // find any lazy values to be loaded
    
    tjc's avatar
    tjc committed
         
    
    tjc's avatar
    tjc committed
        for(int i=0; i<features.size(); i++)
        {
          QualifierVector qualifiers = features.elementAt(i).getQualifiers();
          for(int j=0; j<qualifiers.size(); j++)
          {
            Qualifier qualifier = (Qualifier)qualifiers.get(j);
            if(qualifier instanceof QualifierLazyLoading &&
               !((QualifierLazyLoading)qualifier).isAllLazyValuesLoaded())
            {
    
    tjc's avatar
    tjc committed
              if( ((QualifierLazyLoading)qualifier).getValue(0) instanceof FeatureLocLazyQualifierValue )
    
    tjc's avatar
    tjc committed
                lazySimilarityValues.addAll( ((QualifierLazyLoading)qualifier).getLazyValues() );
              else if( ((QualifierLazyLoading)qualifier).getValue(0) instanceof ClusterLazyQualifierValue )
              {
    
    tjc's avatar
    tjc committed
                List lazyValues = ((QualifierLazyLoading)qualifier).getLazyValues();
                lazyClusterValues.addAll(lazyValues);
    
    tjc's avatar
    tjc committed
              }
    
    tjc's avatar
    tjc committed
              
              ((QualifierLazyLoading)qualifier).setForceLoad(true);
    
    tjc's avatar
    tjc committed
            }
          }
        }
        
        if(lazySimilarityValues.size() > 0 || lazyClusterValues.size() > 0)
        {
    
          int n = JOptionPane.YES_OPTION;  
          if(parent != null)
            n =  JOptionPane.showConfirmDialog(parent,
              "Load and write to file all qualifers from the database?"+
              "\nThis may take a few minutes.",
              "Load All Data",
              JOptionPane.YES_NO_OPTION);
    
    tjc's avatar
    tjc committed
          
          if(n == JOptionPane.YES_OPTION)
          {
            if(parent != null)
              parent.setCursor(new Cursor(Cursor.WAIT_CURSOR));
            final DatabaseDocument document = 
              (DatabaseDocument)((DocumentEntry)entry.getEMBLEntry()).getDocument();
            
            if(lazySimilarityValues.size() > 0)
    
    tjc's avatar
    tjc committed
              FeatureLocLazyQualifierValue.bulkRetrieve(lazySimilarityValues,document);
    
    tjc's avatar
    tjc committed
            
            if(lazyClusterValues.size() > 0)
              ClusterLazyQualifierValue.setClusterFromValueList(lazyClusterValues, document);
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
            if(parent != null)
              parent.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
          }
        }
      }
    
      
      /**
       * Sorts the elements of the vector using a simple O(n^2) selection
       * sort.
       */
      private static void sort(Vector v)
      {
        int smallest;
    
        for (int i = 0; i < v.size (); ++i)
        {
          //find smallest remaining element
          smallest = i;
          for(int j = i + 1 ; j < v.size () ; ++j)
          {
            if(((String)v.get(j)).compareTo( (String)v.get(smallest)) < 0)
              smallest = j;
          }
          //exchange smallest and i
          if (smallest != i)
          {
            final String tmp = (String)v.get(i);
            v.setElementAt (v.get(smallest), i);
            v.setElementAt (tmp, smallest);
          }
        }
      }
    
      
    
    tjc's avatar
    tjc committed
      /**
       * Given a collection of features, determine if these should be
       * shown or hidden in the Artemis display
       * @param features
       */
      public static void defineShowHideGeneFeatures(final FeatureVector features)
      {
    
        final KeyVector keys = 
          features.elementAt(0).getEntry().getEntryInformation().getSortedValidKeys ();
        final Vector showFeatures = new Vector();
        for(int i=0; i<keys.size(); i++)
        {
          String keyStr = ((Key)keys.get(i)).getKeyString();
          if( !hideFeatures.contains(keyStr) )
            showFeatures.add(keyStr);
        }
        
        sort(hideFeatures);
        
    
    tjc's avatar
    tjc committed
        final DefaultListModel showListModel = new DefaultListModel();
        for(int i=0; i<showFeatures.size(); i++)
          showListModel.addElement(showFeatures.get(i));
        final JList displayList = new JList(showListModel);
        
        
        final DefaultListModel hideListModel = new DefaultListModel();
        for(int i=0; i<hideFeatures.size(); i++)
          hideListModel.addElement(hideFeatures.get(i));
        final JList hideList = new JList(hideListModel);
        
        
        final JButton hide_butt = new JButton("HIDE");
        hide_butt.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent e)
          {
            while(!displayList.isSelectionEmpty())
            {
              final String hideKey = (String)displayList.getSelectedValue();
              
              hideFeatures.add(hideKey);
              if(showFeatures.contains(hideKey))
                showFeatures.remove(hideKey);
    
              
              sort(hideFeatures);
              hideListModel.add(hideFeatures.indexOf(hideKey), hideKey);
              showListModel.removeElement(hideKey);
    
    tjc's avatar
    tjc committed
            }
          }
        });
    
        Box bdown = Box.createVerticalBox();
        bdown.add(new JLabel("Features Displayed:"));
        bdown.add(new JScrollPane(displayList));
        bdown.add(hide_butt);
        
        final JPanel hideShowPanel = new JPanel(new BorderLayout());
        hideShowPanel.add(bdown, BorderLayout.CENTER);
    
        
        final JButton show_butt = new JButton("SHOW");
        show_butt.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent e)
          {
            while(!hideList.isSelectionEmpty())
            {
              final String showKey = (String)hideList.getSelectedValue();
              
              if(hideFeatures.contains(showKey))
                hideFeatures.remove(showKey);
              showFeatures.add(showKey);
    
              sort(showFeatures);
              
              showListModel.add(showFeatures.indexOf(showKey), showKey);
              hideListModel.removeElement(showKey);
    
    tjc's avatar
    tjc committed
            }
          }
        });
    
        bdown = Box.createVerticalBox();
        bdown.add(Box.createVerticalGlue());
        bdown.add(new JLabel("Features Hidden:"));
        bdown.add(new JScrollPane(hideList));
        bdown.add(show_butt);
        hideShowPanel.add(bdown, BorderLayout.EAST);
    
    tjc's avatar
    tjc committed
        
        hideShowPanel.add(showObsolete, BorderLayout.SOUTH);
    
    tjc's avatar
    tjc committed
    
        int select = JOptionPane.showConfirmDialog(null, hideShowPanel,
                                "Gene Model Features Displayed...",
                                 JOptionPane.OK_CANCEL_OPTION,
                                 JOptionPane.QUESTION_MESSAGE);
    
        if(select == JOptionPane.CANCEL_OPTION)
          return;
        
        showHideGeneFeatures(features);
      }
      
    
    tjc's avatar
    tjc committed
      /**
       * Based on the hidenFeatures and showFeatures set the GFFStreamFeatures
       * visibility
       * @param features
       */
    
    tjc's avatar
    tjc committed
      public static void showHideGeneFeatures(final FeatureVector features)
      {
        for(int i=0; i<features.size(); i++)
        {
          final Feature feature = features.elementAt(i).getEmblFeature();
          
          if(feature instanceof GFFStreamFeature)
          {
    
    tjc's avatar
    tjc committed
            if(isObsolete((GFFStreamFeature)feature))
            {
              if(!showObsolete.isSelected())
              {
                ((GFFStreamFeature)feature).setVisible(false);
                continue;
              }
            }
            
    
    tjc's avatar
    tjc committed
            final String key = feature.getKey().getKeyString();
            if(hideFeatures.contains(key))
              ((GFFStreamFeature)feature).setVisible(false);
    
    tjc's avatar
    tjc committed
              ((GFFStreamFeature)feature).setVisible(true);
          }
        }
      }
      
    
    tjc's avatar
    tjc committed
      /**
       * Determine based on the feature given if a feature is hidden
       * @param key
       * @return
       */
    
    tjc's avatar
    tjc committed
      public static boolean isHiddenFeature(final String key)
      {
        return hideFeatures.contains(key);
      }
      
    
    tjc's avatar
    tjc committed
      /**
       * Test if this feature is obsolete
       * @param feature
       * @return
       */
      public static boolean isObsolete(final uk.ac.sanger.artemis.io.GFFStreamFeature feature)
      {
        Qualifier qualifier = feature.getQualifierByName("isObsolete");
        if(qualifier == null)
          return false;
        
        if( ((String)qualifier.getValues().get(0)).equals("true") )
          return true;
        
        return false;
      }
    
    tjc's avatar
    tjc committed
      /**
       * 
    
    tjc's avatar
    tjc committed
       */
    
      public static Vector duplicateGeneModel(final JFrame frame,
    
    tjc's avatar
    tjc committed
          final FeatureVector features_to_duplicate,
    
    tjc's avatar
    tjc committed
          final EntryGroup entry_group)
      {
        final Vector duplicatedGenes = new Vector();
    
        final Vector newGenes = new Vector();
    
    tjc's avatar
    tjc committed
        for (int i = 0 ; i < features_to_duplicate.size () ; ++i) 
        {
          final uk.ac.sanger.artemis.Feature this_feature = 
                               features_to_duplicate.elementAt(i);
          final GFFStreamFeature gffFeature = (GFFStreamFeature)this_feature.getEmblFeature();
          if(duplicatedGenes.contains(gffFeature.getChadoGene()))
            continue;
          
          duplicatedGenes.add(gffFeature.getChadoGene());
          
          try 
          {
            GFFStreamFeature gene = (GFFStreamFeature)gffFeature.getChadoGene().getGene();
            uk.ac.sanger.artemis.Feature newGeneFeature = ((uk.ac.sanger.artemis.Feature)
                gene.getUserData()).duplicate (true);
            
            final ChadoCanonicalGene chadoGene = gffFeature.getChadoGene();
            final ChadoCanonicalGene newchadoGene = new ChadoCanonicalGene();
    
            newGenes.add(newchadoGene);
    
    tjc's avatar
    tjc committed
            ((GFFStreamFeature)newGeneFeature.getEmblFeature()).setChadoGene(newchadoGene);
            newchadoGene.setGene(newGeneFeature.getEmblFeature());
            
            final List transcripts = chadoGene.getTranscripts();
            for(int j=0; j<transcripts.size(); j++)
            {
              final GFFStreamFeature transcript = (GFFStreamFeature)transcripts.get(j);
              final String transcriptName =
                (String)transcript.getQualifierByName("ID").getValues().get(0);
              
              uk.ac.sanger.artemis.Feature newTranscriptFeature = 
                duplicateFeature(transcript, newchadoGene);
              newchadoGene.addTranscript(newTranscriptFeature.getEmblFeature());
              final String newTranscriptName =
                (String)newTranscriptFeature.getQualifierByName("ID").getValues().get(0);
              
              List newFeatures;
              
              newFeatures= duplicateFeatures(chadoGene.get3UtrOfTranscript(transcriptName), newchadoGene);
              for(int k=0; k<newFeatures.size(); k++)
    
    tjc's avatar
    tjc committed
              {
                uk.ac.sanger.artemis.Feature utrFeature = 
                  (uk.ac.sanger.artemis.Feature)newFeatures.get(k);
                newchadoGene.add3PrimeUtr(newTranscriptName, utrFeature.getEmblFeature());
              }
    
    tjc's avatar
    tjc committed
              
              newFeatures = duplicateFeatures(chadoGene.get5UtrOfTranscript(transcriptName), newchadoGene);
              for(int k=0; k<newFeatures.size(); k++)
    
    tjc's avatar
    tjc committed
              {
                uk.ac.sanger.artemis.Feature utrFeature = 
                  (uk.ac.sanger.artemis.Feature)newFeatures.get(k);
                newchadoGene.add5PrimeUtr(newTranscriptName, utrFeature.getEmblFeature());
              }
    
    tjc's avatar
    tjc committed
              
              newFeatures = duplicateFeatures(chadoGene.getOtherFeaturesOfTranscript(transcriptName), newchadoGene);
              for(int k=0; k<newFeatures.size(); k++)
    
    tjc's avatar
    tjc committed
              {
                uk.ac.sanger.artemis.Feature otherFeature = 
                  (uk.ac.sanger.artemis.Feature)newFeatures.get(k);
                newchadoGene.addOtherFeatures(newTranscriptName, otherFeature.getEmblFeature());
              }
    
    tjc's avatar
    tjc committed
    
              newFeatures = duplicateFeatures(chadoGene.getSplicedFeaturesOfTranscript(transcriptName), newchadoGene);
              for(int k=0; k<newFeatures.size(); k++)
              {
                uk.ac.sanger.artemis.Feature splicedFeature = 
                  (uk.ac.sanger.artemis.Feature)newFeatures.get(k);
                newchadoGene.addSplicedFeatures(newTranscriptName, splicedFeature.getEmblFeature());
              }
              
              uk.ac.sanger.artemis.Feature newProtein = 
                duplicateFeature(chadoGene.getProteinOfTranscript(transcriptName), newchadoGene);
              if(newProtein != null)
                newchadoGene.addProtein(newTranscriptName, newProtein.getEmblFeature());
              
            }
          } 
          catch (ReadOnlyException e) {}
          catch(InvalidRelationException e) {}
        }
        
        duplicatedGenes.clear();
    
        return newGenes;
    
    tjc's avatar
    tjc committed
      }
      
      
      private static List duplicateFeatures(final List featuresOfTranscript,
                                     final ChadoCanonicalGene chadoGene) 
              throws ReadOnlyException
      {
        final List newFeatures = new Vector();
        
        if(featuresOfTranscript == null)
          return newFeatures;
        
        for(int i=0; i<featuresOfTranscript.size(); i++)
          newFeatures.add(duplicateFeature(
              (GFFStreamFeature)featuresOfTranscript.get(i), chadoGene));
      
        return newFeatures;
      }
      
      private static uk.ac.sanger.artemis.Feature duplicateFeature(
              final Feature feature, final ChadoCanonicalGene chadoGene) 
              throws ReadOnlyException
      {
        if(feature == null)
          return null;
        uk.ac.sanger.artemis.Feature newFeature = 
          ((uk.ac.sanger.artemis.Feature)feature.getUserData()).duplicate(true);
        ((GFFStreamFeature)newFeature.getEmblFeature()).setChadoGene(chadoGene);
        if(isHiddenFeature(newFeature.getKey().getKeyString()))
          ((GFFStreamFeature)newFeature.getEmblFeature()).setVisible(false);
        /*
        try
        {
          final QualifierVector qv = newFeature.getQualifiers().copy();
          
          for(int i=0; i<qv.size(); i++)
          {
            final Qualifier qualifier = (Qualifier)qv.elementAt(i);
            if(!qualifier.getName().equals("ID") &&
               !qualifier.getName().equals("Parent") &&
               !qualifier.getName().equals("Derives_from") &&
               ChadoTransactionManager.isSpecialTag(qualifier.getName()))
              newFeature.getQualifiers().removeQualifierByName(qualifier.getName());
          }
    
          newFeature.set(newFeature.getKey(), newFeature.getLocation(), qv);
        }
        catch(EntryInformationException e)
        {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
        catch(OutOfRangeException e)
        {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
        */
        return newFeature;
      }
      
      /**
       * Create gene model from base selection
    
    tjc's avatar
    tjc committed
       * @param frame
       * @param selection
       * @param entry_group
       * @param goto_event_source
       */
      public static void createGeneModel(final JFrame frame,
          final Selection selection,
          final EntryGroup entry_group,
          final GotoEventSource goto_event_source)
      {
        if(!SelectionMenu.checkForSelectionRange(frame, selection))
          return;
        final MarkerRange range = selection.getMarkerRange ();
        final Entry default_entry = entry_group.getDefaultEntry ();
    
        if (default_entry == null) 
        {
          new MessageDialog (frame, "There is no default entry");
          return;
        }
        
        final QualifierVector qualifiers = new QualifierVector();
        final String uniquename = promptForUniquename(entry_group, 
                                       range.isForwardMarker());
        final Qualifier qualifier = new Qualifier("ID", uniquename);
        qualifiers.add(qualifier);
        
        try
        {
    
    tjc's avatar
    tjc committed
          final FeatureVector newFeatures = new FeatureVector();
    
    tjc's avatar
    tjc committed
          final Location new_location = range.createLocation ();
          final Key key = new Key("gene");
          final uk.ac.sanger.artemis.Feature geneFeature = 
              default_entry.createFeature(key, new_location, qualifiers);
    
    tjc's avatar
    tjc committed
          newFeatures.add(geneFeature);
          
    
    tjc's avatar
    tjc committed
          final ChadoCanonicalGene chadoGene = new ChadoCanonicalGene();
          chadoGene.setGene(geneFeature.getEmblFeature());
          ((uk.ac.sanger.artemis.io.GFFStreamFeature) 
              (geneFeature.getEmblFeature())).setChadoGene(chadoGene);
          
          // create transcript
          uk.ac.sanger.artemis.Feature transcript = 
            GeneViewerPanel.createTranscript(chadoGene, entry_group);
    
    tjc's avatar
    tjc committed
          newFeatures.add(transcript);
    
    tjc's avatar
    tjc committed
          ((uk.ac.sanger.artemis.io.GFFStreamFeature)
              (transcript.getEmblFeature())).setChadoGene(chadoGene);
          final String transcriptId = 
             (String)transcript.getQualifierByName("ID").getValues().get(0);
          
          // add exon
          GeneViewerPanel.addExonFeature(chadoGene, entry_group, 
    
    tjc's avatar
    tjc committed
              null, new_location.getTotalRange(), transcriptId, selection, 
    
    tjc's avatar
    tjc committed
              new Key(DatabaseDocument.EXONMODEL), null);
          
          // add protein
          uk.ac.sanger.artemis.Feature polypep =
            GeneViewerPanel.addProteinFeature(chadoGene, entry_group, transcriptId, transcript);
    
    tjc's avatar
    tjc committed
          newFeatures.add(polypep);
    
    tjc's avatar
    tjc committed
          
    
    tjc's avatar
    tjc committed
          showHideGeneFeatures(newFeatures);
    
    tjc's avatar
    tjc committed
          selection.clear();
          selection.add(polypep);
          
          EditMenu.editSelectedFeatures(entry_group, selection, 
              goto_event_source, polypep, null, null);
        }
        catch(ReadOnlyException e)
        {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
        catch(EntryInformationException e)
        {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
        catch(OutOfRangeException e)
        {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      }
      
      /**
       * Prompt the user for an ID
       * @return
       */
      public static String promptForUniquename(final EntryGroup entry_group,
                                                final boolean is_forward)
      {
        final Entry default_entry = entry_group.getDefaultEntry ();
        String id = null;
        
        if(default_entry.getEMBLEntry() instanceof 
            uk.ac.sanger.artemis.io.DatabaseDocumentEntry)
        {  
          while(id == null ||
                id.equals("") ||
                id.equals("to_be_set"))
          {
            String msg = "Provide a unique ID ";
            
            if(!is_forward)
              msg = msg + "for reverse strand : ";
            else
              msg = msg + ": ";
            
            id = JOptionPane.showInputDialog(null,
                               msg,
                               "ID missing ",
                               JOptionPane.QUESTION_MESSAGE).trim();
            
            if(!isUniqueID(entry_group, id))
            {
              JOptionPane.showMessageDialog(null, 
                  "ID "+id+" not unique.\nEnter a unique ID.", 
                  "ID Not Unique", 
                  JOptionPane.WARNING_MESSAGE);
              id = null;
            }
          }
        }
        return id;
      }
      
      /**
       * Test to ensure ID (chado uniquename) is unique.
       * @param entry_group
       * @param id
       * @return
       */
      private static boolean isUniqueID(final EntryGroup entry_group,
                                        final String id)
      {
        final FeaturePredicate predicate =
          new FeatureKeyQualifierPredicate(null, "ID", id, 
                                           false, true);
        final FeatureVector features = entry_group.getAllFeatures();
        for(int i=0; i<features.size(); i++)
        {
          uk.ac.sanger.artemis.Feature feature = features.elementAt(i);
          if(predicate.testPredicate(feature))
            return false;
          
        }
        return true;
      }
      
    
    tjc's avatar
    tjc committed
      /**
       * Given an group of entries determine if they contain a database entry
       * @param entryGroup
       * @return
       */
      public static boolean isDatabaseEntry(final EntryGroup entryGroup)
      {
        final EntryVector entries = entryGroup.getActiveEntries();
        
        for(int i=0; i<entries.size(); i++)
        {
          if( entries.elementAt(i).getEMBLEntry() instanceof DatabaseDocumentEntry )
            return true;
        }
        return false;
      }
      
    
    tjc's avatar
    tjc committed
      
      /**
       * Given a feature determine if it belongs to a database entry
       * @param entryGroup
       * @return
       */
      public static boolean isDatabaseEntry(final Feature feature)
      {
        if( feature.getEntry() instanceof DatabaseDocumentEntry )
          return true;
    
        return false;
      }
      
    
    tjc's avatar
    tjc committed
    
      private static void deleteFeature(uk.ac.sanger.artemis.Feature feature)
          throws ReadOnlyException
      {
        if(feature != null && feature.getEntry() != null)
          feature.removeFromEntry();
      }
    
      /**
       * Delete feature and children in a chado gene model
       * @param feature
       * @param chado_gene
       * @throws ReadOnlyException
       */
      public static void deleteAllFeature(uk.ac.sanger.artemis.Feature feature,
          final ChadoCanonicalGene chado_gene) throws ReadOnlyException
      {
        Set children = chado_gene.getChildren(feature.getEmblFeature());
        deleteFeature(feature);
        chado_gene.deleteFeature(feature.getEmblFeature());
    
        Feature embl_feature;
        Iterator it = children.iterator();
    
        while(it.hasNext())
        {
          embl_feature = (Feature) it.next();
          deleteFeature((uk.ac.sanger.artemis.Feature) embl_feature.getUserData());
          chado_gene.deleteFeature(embl_feature);
        }
      }
    
    tjc's avatar
    tjc committed
    
      /**
       * Check gene model boundaries for any inconsistencies
       * @param chado_gene
       * @return true is gene model ranges are correct
       */
      public static boolean isBoundaryOK(final ChadoCanonicalGene chado_gene)
      {
        final Range geneRange = chado_gene.getGene().getLocation().getTotalRange();
        final List transcripts = chado_gene.getTranscripts();
        int geneStart = Integer.MAX_VALUE;
        int geneEnd = -1;
        
        for(int i=0; i<transcripts.size(); i++)
        {
          final Feature transcript = (Feature)transcripts.get(i);
          final Range transcriptRange = transcript.getLocation().getTotalRange();
          int transcriptStart = Integer.MAX_VALUE;
          int transcriptEnd = -1;
          
          if(transcriptRange.getStart() < geneRange.getStart() ||
             transcriptRange.getEnd()   > geneRange.getEnd())
            return false;
          
          if(transcriptRange.getStart() < geneStart)
            geneStart = transcriptRange.getStart();
          if(transcriptRange.getEnd() > geneEnd)
            geneEnd = transcriptRange.getEnd();
          
          final Feature protein = 
            chado_gene.getProteinOfTranscript(GeneUtils.getUniqueName(transcript));
          String proteinName = null;
          if(protein != null)
            proteinName = GeneUtils.getUniqueName(protein);
          
          final Set children = chado_gene.getChildren(transcript);
          final Iterator it = children.iterator();
          while(it.hasNext())
          {
            final Feature feature = (Feature) it.next();
            final Range childRange = feature.getLocation().getTotalRange();
            if(childRange.getStart() < transcriptRange.getStart() ||
               childRange.getEnd()   > transcriptRange.getEnd())
              return false;
    
            if(proteinName != null &&
               GeneUtils.getUniqueName(feature).equals(proteinName))
              continue;
            
            if(childRange.getStart() < transcriptStart)
              transcriptStart = childRange.getStart();
            if(childRange.getEnd() > transcriptEnd )
              transcriptEnd = childRange.getEnd();
          }
          
          if((transcriptRange.getStart() != transcriptStart && transcriptStart < Integer.MAX_VALUE) ||
             (transcriptRange.getEnd()   != transcriptEnd   && transcriptEnd   > -1))
            return false;
    
          if(protein != null)
          {
            final Range proteinRange = protein.getLocation().getTotalRange();
            if(proteinRange.getStart() != transcriptStart &&
               proteinRange.getEnd()   != transcriptEnd)
              return false;
          }
        }
        
        if((geneRange.getStart() != geneStart && geneStart < Integer.MAX_VALUE) ||
           (geneRange.getEnd()   != geneEnd   && geneEnd   > -1))
          return false;
        
        return true;
      }
    
    tjc's avatar
    tjc committed
      
      /**
       * Adjust transcript and gene boundaries
       * @param chado_gene
       */
      public static void checkGeneBoundary(final ChadoCanonicalGene chado_gene)
      {
        final List transcripts = chado_gene.getTranscripts();
        int gene_start = Integer.MAX_VALUE;
        int gene_end = -1;
        
        Range range;
        for(int i=0; i<transcripts.size(); i++)
        {
          final Feature transcript = (Feature)transcripts.get(i);
          range = checkTranscriptBoundary(
              (uk.ac.sanger.artemis.Feature)transcript.getUserData(), chado_gene);
    
    tjc's avatar
    tjc committed
          if(range != null && range.getStart() < gene_start)
    
    tjc's avatar
    tjc committed
            gene_start = range.getStart();
    
    tjc's avatar
    tjc committed
          if(range != null && range.getEnd() > gene_end)
    
    tjc's avatar
    tjc committed
            gene_end = range.getEnd();
        }
        
    
    tjc's avatar
    tjc committed
        if(gene_end == -1 && gene_start == Integer.MAX_VALUE)
          return;
        
        setLocation(chado_gene.getGene(), gene_start, gene_end);
    
    tjc's avatar
    tjc committed
      /**
       * Check and adjust transcript boundary
       * @param transcript
       * @param chado_gene