1 /* GeneUtils.java
2  *
3  * This file is part of Artemis
4  * Copyright (C) 2007  Genome Research Limited
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  **/
21 package uk.ac.sanger.artemis.components.genebuilder;
22 
23 import java.awt.BorderLayout;
24 import java.awt.Cursor;
25 import java.awt.event.ActionEvent;
26 import java.awt.event.ActionListener;
27 import java.util.Collection;
28 import java.util.Enumeration;
29 import java.util.Hashtable;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Set;
33 import java.util.Vector;
34 
35 import javax.swing.Box;
36 import javax.swing.DefaultListModel;
37 import javax.swing.JButton;
38 import javax.swing.JCheckBox;
39 import javax.swing.JFrame;
40 import javax.swing.JLabel;
41 import javax.swing.JList;
42 import javax.swing.JOptionPane;
43 import javax.swing.JPanel;
44 import javax.swing.JScrollPane;
45 
46 import org.gmod.schema.general.DbXRef;
47 import org.gmod.schema.sequence.FeatureCvTerm;
48 import org.gmod.schema.sequence.FeatureDbXRef;
49 import org.gmod.schema.sequence.FeaturePub;
50 import org.gmod.schema.sequence.FeatureSynonym;
51 
52 import uk.ac.sanger.artemis.chado.ClusterLazyQualifierValue;
53 import uk.ac.sanger.artemis.chado.FeatureForUpdatingResidues;
54 import uk.ac.sanger.artemis.chado.FeatureLocLazyQualifierValue;
55 import uk.ac.sanger.artemis.components.EditMenu;
56 import uk.ac.sanger.artemis.components.MessageDialog;
57 import uk.ac.sanger.artemis.components.SelectionMenu;
58 import uk.ac.sanger.artemis.io.ChadoCanonicalGene;
59 import uk.ac.sanger.artemis.io.DatabaseDocumentEntry;
60 import uk.ac.sanger.artemis.io.DatabaseInferredFeature;
61 import uk.ac.sanger.artemis.io.DocumentEntry;
62 import uk.ac.sanger.artemis.io.EntryInformationException;
63 import uk.ac.sanger.artemis.io.Feature;
64 import uk.ac.sanger.artemis.io.GFF3Encoder;
65 import uk.ac.sanger.artemis.io.GFFDocumentEntry;
66 import uk.ac.sanger.artemis.io.GFFStreamFeature;
67 import uk.ac.sanger.artemis.io.InvalidRelationException;
68 import uk.ac.sanger.artemis.io.Key;
69 import uk.ac.sanger.artemis.io.KeyVector;
70 import uk.ac.sanger.artemis.io.Location;
71 import uk.ac.sanger.artemis.io.Qualifier;
72 import uk.ac.sanger.artemis.io.QualifierLazyLoading;
73 import uk.ac.sanger.artemis.io.QualifierVector;
74 import uk.ac.sanger.artemis.io.Range;
75 import uk.ac.sanger.artemis.io.RangeVector;
76 import uk.ac.sanger.artemis.sequence.MarkerRange;
77 import uk.ac.sanger.artemis.util.ByteBuffer;
78 import uk.ac.sanger.artemis.util.DatabaseDocument;
79 import uk.ac.sanger.artemis.util.OutOfRangeException;
80 import uk.ac.sanger.artemis.util.ReadOnlyException;
81 import uk.ac.sanger.artemis.util.StringVector;
82 import uk.ac.sanger.artemis.Entry;
83 import uk.ac.sanger.artemis.EntryGroup;
84 import uk.ac.sanger.artemis.EntryVector;
85 import uk.ac.sanger.artemis.FeatureKeyQualifierPredicate;
86 import uk.ac.sanger.artemis.FeaturePredicate;
87 import uk.ac.sanger.artemis.FeatureVector;
88 import uk.ac.sanger.artemis.GotoEventSource;
89 import uk.ac.sanger.artemis.Options;
90 import uk.ac.sanger.artemis.Selection;
91 
92 
93 public class GeneUtils
94 {
95   private static Vector<String> hideFeatures = new Vector<String>();
96   private static JCheckBox showObsolete = new JCheckBox("Show Obsolete Features",false);
97   private static String nonCodingTranscripts[] =
98                                 { "tRNA", "rRNA", "snRNA", "snoRNA", "ncRNA", "scRNA" };
99   private static StringVector featuresToUpdateResidues =
100     Options.getOptions().getOptionValues("sequence_update_features");
101 
102   static
103   {
104     hideFeatures.add("polypeptide");
105     hideFeatures.add(DatabaseDocument.TRANSCRIPT);
106     hideFeatures.add("pseudogenic_transcript");
107   }
108 
109   /**
110    * Used when a whole sequence is loaded in and the features are loaded
111    * lazily
112    * @param feature
113    */
addLazyQualifiers(final GFFStreamFeature feature)114   public static void addLazyQualifiers(final GFFStreamFeature feature)
115   {
116     if(feature.isLazyLoaded() || feature.getChadoLazyFeature() == null)
117       return;
118 
119     // synonyms
120     final Collection featureSynonyms = feature.getChadoLazyFeature().getFeatureSynonyms();
121 
122     final Iterator it = featureSynonyms.iterator();
123     while(it.hasNext())
124     {
125       final FeatureSynonym featureSynonym = (FeatureSynonym) it.next();
126       final String name = featureSynonym.getSynonym().getCvTerm().getName();
127       String value = featureSynonym.getSynonym().getName();
128 
129       if(!featureSynonym.isCurrent())
130         value.concat(GFF3Encoder.encode(";current=false"));
131 
132       Qualifier qualifier = feature.getQualifiers().getQualifierByName(name);
133       if(qualifier == null)
134         qualifier = new Qualifier(name, value);
135       else
136         qualifier.addValue(value);
137 
138       feature.getQualifiers().setQualifier(qualifier);
139     }
140 
141     // dbxrefs
142     if(feature.getQualifierByName("Dbxref") == null)
143     {
144       DbXRef dbxref = feature.getChadoLazyFeature().getDbXRef();
145       if(dbxref != null)
146       {
147         String value = dbxref.getDb().getName() + ":" +
148                        dbxref.getAccession();
149         feature.getQualifiers().setQualifier(new Qualifier("Dbxref", value));
150 
151         if(feature.isReadOnly() && feature.getKey().equals("polypeptide_domain"))
152         {
153           value= "protein motif:"+value;
154           feature.getQualifiers().setQualifier(new Qualifier("inference", value));
155         }
156       }
157     }
158 
159 
160     final Collection featureDbXRefs = feature.getChadoLazyFeature().getFeatureDbXRefs();
161     final Iterator it2 = featureDbXRefs.iterator();
162     while(it2.hasNext())
163     {
164       final FeatureDbXRef featureDbXRef = (FeatureDbXRef) it2.next();
165       String value = featureDbXRef.getDbXRef().getDb().getName() + ":" +
166                      featureDbXRef.getDbXRef().getAccession();
167 
168       Qualifier qualifier = feature.getQualifiers().getQualifierByName("Dbxref");
169       if(qualifier == null)
170         qualifier = new Qualifier("Dbxref", value);
171       else
172         qualifier.addValue(value);
173       feature.getQualifiers().setQualifier(qualifier);
174     }
175 
176 
177     // feature cvterms (GO, product....)
178     final Collection featureCvTerms = feature.getChadoLazyFeature().getFeatureCvTerms();
179     if(featureCvTerms != null)
180     {
181       final Iterator it3 = featureCvTerms.iterator();
182       while(it3.hasNext())
183       {
184         FeatureCvTerm featureCvTerm = (FeatureCvTerm)it3.next();
185         List featureCvTermDbXRefList = null;
186 
187         if(featureCvTerm.getFeatureCvTermDbXRefs() != null)
188           featureCvTermDbXRefList = new Vector(featureCvTerm.getFeatureCvTermDbXRefs());
189 
190         List featureCvTermPubList = null;
191 
192         if(featureCvTerm.getFeatureCvTermPubs() != null)
193           featureCvTermPubList = new Vector(featureCvTerm.getFeatureCvTermPubs());
194 
195         ByteBuffer this_buff = new ByteBuffer();
196         DatabaseDocument.appendControlledVocabulary(this_buff, null, featureCvTerm,
197                                  featureCvTermDbXRefList,featureCvTermPubList, null, false);
198 
199         final String qualifierString = new String(this_buff.getBytes());
200         int ind = qualifierString.indexOf('=');
201         final String name  = qualifierString.substring(0, ind);
202         final String value = GFF3Encoder.decode(
203           qualifierString.substring(ind+1, qualifierString.length()-1));
204 
205         Qualifier qualifier = feature.getQualifiers().getQualifierByName(name);
206         if(qualifier == null)
207           qualifier = new Qualifier(name, value);
208         else
209           qualifier.addValue(value);
210         feature.getQualifiers().setQualifier(qualifier);
211       }
212     }
213     // feature pubs - literature
214     final Collection featurePubs = feature.getChadoLazyFeature().getFeaturePubs();
215 
216     if(featurePubs != null)
217     {
218       final Iterator it4 = featurePubs.iterator();
219       while(it4.hasNext())
220       {
221         FeaturePub featurePub = (FeaturePub) it4.next();
222 
223         Qualifier qualifier = feature.getQualifiers().getQualifierByName(
224             "literature");
225         if(qualifier == null)
226           qualifier = new Qualifier("literature", featurePub.getPub()
227               .getUniqueName());
228         else
229           qualifier.addValue(featurePub.getPub().getUniqueName());
230         feature.getQualifiers().setQualifier(qualifier);
231       }
232     }
233 
234     feature.setLazyLoaded(true);
235   }
236 
237   /**
238    * Used to reverse complement all the gene model features
239    * @param chadoGene
240    */
complementGeneModel(final ChadoCanonicalGene chadoGene)241   public static void complementGeneModel(final ChadoCanonicalGene chadoGene)
242   {
243     if(chadoGene == null)
244       return;
245     try
246     {
247       final Feature gene = chadoGene.getGene();
248       final boolean complement = gene.getLocation().isComplement();
249       gene.setLocation(gene.getLocation().getComplement());
250       final Set kids = chadoGene.getChildren(gene);
251       final Iterator it = kids.iterator();
252       while(it.hasNext())
253       {
254         final Feature f = (Feature)it.next();
255         final RangeVector rv = f.getLocation().getRanges();
256         rv.reverse();
257         f.setLocation(new Location(rv, !complement));
258       }
259     }
260     catch(ReadOnlyException e)
261     {
262       e.printStackTrace();
263     }
264     catch(OutOfRangeException e)
265     {
266       e.printStackTrace();
267     }
268   }
269 
addSegment(final GFFStreamFeature feature, final RangeVector rangesToAdd, final String transcriptName)270   public static void addSegment(final GFFStreamFeature feature,
271                                 final RangeVector rangesToAdd,
272                                 final String transcriptName)
273          throws ReadOnlyException, EntryInformationException
274   {
275     // add new ID
276     final Hashtable id_store = feature.getSegmentRangeStore();
277     String prefix[] = null;
278     Enumeration enum_ids = id_store.keys();
279     while(enum_ids.hasMoreElements())
280     {
281       String id = (String) enum_ids.nextElement();
282       prefix = feature.getPrefix(id, ':');
283       if(prefix[0] != null)
284         break;
285     }
286 
287     // USE PREFIX TO CREATE NEW ID
288     RangeVector rv = (RangeVector)feature.getLocation().getRanges().clone();
289     for(int i=0; i<rangesToAdd.size(); i++)
290     {
291       final Range range = (Range) rangesToAdd.elementAt(i);
292       final String ID;
293       if(prefix[0] != null)
294       {
295         int auto_num = feature.getAutoNumber(prefix[0], ':');
296         ID = prefix[0] + ":" + auto_num;
297         feature.getSegmentRangeStore().put(ID, range);
298       }
299       else
300       {
301         String key = feature.getKey().toString();
302         ID = transcriptName + ":" + key + ":1";
303         feature.getSegmentRangeStore().put(ID, range);
304       }
305 
306       if(!rv.containsRange(range))
307         rv.add(range);
308     }
309 
310     feature.setQualifier(new Qualifier("ID", feature.getSegmentID( rv )));
311   }
312 
313   /**
314    * Used when writing the database entry to a file. This routine
315    * forces lazy-loading qualifier values to be read in full.
316    * @param entry
317    * @param parent
318    */
lazyLoadAll(final Entry entry, final JFrame parent)319   public static void lazyLoadAll(final Entry entry, final JFrame parent)
320   {
321     final List lazySimilarityValues = new Vector();
322     final List lazyClusterValues = new Vector();
323     final FeatureVector features = entry.getAllFeatures();
324     // find any lazy values to be loaded
325 
326     for(int i=0; i<features.size(); i++)
327     {
328       QualifierVector qualifiers = features.elementAt(i).getQualifiers();
329       for(int j=0; j<qualifiers.size(); j++)
330       {
331         Qualifier qualifier = (Qualifier)qualifiers.get(j);
332         if(qualifier instanceof QualifierLazyLoading &&
333            !((QualifierLazyLoading)qualifier).isAllLazyValuesLoaded())
334         {
335           if( ((QualifierLazyLoading)qualifier).getValue(0) instanceof FeatureLocLazyQualifierValue )
336             lazySimilarityValues.addAll( ((QualifierLazyLoading)qualifier).getLazyValues() );
337           else if( ((QualifierLazyLoading)qualifier).getValue(0) instanceof ClusterLazyQualifierValue )
338           {
339             List lazyValues = ((QualifierLazyLoading)qualifier).getLazyValues();
340             lazyClusterValues.addAll(lazyValues);
341           }
342 
343           ((QualifierLazyLoading)qualifier).setForceLoad(true);
344         }
345       }
346     }
347 
348     if(lazySimilarityValues.size() > 0 || lazyClusterValues.size() > 0)
349     {
350       int n = JOptionPane.YES_OPTION;
351       if(parent != null)
352         n =  JOptionPane.showConfirmDialog(parent,
353           "Load and write to file all qualifers from the database?"+
354           "\nThis may take a few minutes.",
355           "Load All Data",
356           JOptionPane.YES_NO_OPTION);
357 
358       if(n == JOptionPane.YES_OPTION)
359       {
360         if(parent != null)
361           parent.setCursor(new Cursor(Cursor.WAIT_CURSOR));
362         final DatabaseDocument document =
363           (DatabaseDocument)((DocumentEntry)entry.getEMBLEntry()).getDocument();
364 
365         if(lazySimilarityValues.size() > 0)
366           FeatureLocLazyQualifierValue.bulkRetrieve(lazySimilarityValues,document);
367 
368         if(lazyClusterValues.size() > 0)
369           ClusterLazyQualifierValue.setClusterFromValueList(lazyClusterValues, document);
370 
371         for(int i=0; i<features.size(); i++)
372         {
373           GFFStreamFeature feature = (GFFStreamFeature)(features.elementAt(i).getEmblFeature());
374           if(feature.isReadOnly() &&
375              feature.getKey().equals("polypeptide_domain") &&
376              feature.getChadoLazyFeature() != null)
377           {
378             // load dbxrefs for domains
379             if(feature.getQualifierByName("Dbxref") == null)
380             {
381               DbXRef dbxref = feature.getChadoLazyFeature().getDbXRef();
382               if(dbxref != null)
383               {
384                 String value = dbxref.getDb().getName() + ":" +
385                                dbxref.getAccession();
386                 feature.getQualifiers().setQualifier(new Qualifier("Dbxref", value));
387 
388                 value= "protein motif:"+value;
389                 feature.getQualifiers().setQualifier(new Qualifier("inference", value));
390               }
391             }
392           }
393         }
394         if(parent != null)
395           parent.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
396       }
397     }
398   }
399 
400   /**
401    * Sorts the elements of the vector using a simple O(n^2) selection
402    * sort.
403    */
sort(Vector v)404   private static void sort(Vector v)
405   {
406     int smallest;
407 
408     for (int i = 0; i < v.size (); ++i)
409     {
410       //find smallest remaining element
411       smallest = i;
412       for(int j = i + 1 ; j < v.size () ; ++j)
413       {
414         if(((String)v.get(j)).compareTo( (String)v.get(smallest)) < 0)
415           smallest = j;
416       }
417       //exchange smallest and i
418       if (smallest != i)
419       {
420         final String tmp = (String)v.get(i);
421         v.setElementAt (v.get(smallest), i);
422         v.setElementAt (tmp, smallest);
423       }
424     }
425   }
426 
427 
428   /**
429    * Given a collection of features, determine if these should be
430    * shown or hidden in the Artemis display
431    * @param features
432    */
defineShowHideGeneFeatures(final FeatureVector features)433   public static void defineShowHideGeneFeatures(final FeatureVector features)
434   {
435     final KeyVector keys =
436       features.elementAt(0).getEntry().getEntryInformation().getSortedValidKeys ();
437     final Vector showFeatures = new Vector();
438     for(int i=0; i<keys.size(); i++)
439     {
440       String keyStr = ((Key)keys.get(i)).getKeyString();
441       if( !hideFeatures.contains(keyStr) )
442         showFeatures.add(keyStr);
443     }
444 
445     sort(hideFeatures);
446 
447     final DefaultListModel showListModel = new DefaultListModel();
448     for(int i=0; i<showFeatures.size(); i++)
449       showListModel.addElement(showFeatures.get(i));
450     final JList displayList = new JList(showListModel);
451 
452 
453     final DefaultListModel hideListModel = new DefaultListModel();
454     for(int i=0; i<hideFeatures.size(); i++)
455       hideListModel.addElement(hideFeatures.get(i));
456     final JList hideList = new JList(hideListModel);
457 
458 
459     final JButton hide_butt = new JButton("HIDE");
460     hide_butt.addActionListener(new ActionListener()
461     {
462       public void actionPerformed(ActionEvent e)
463       {
464         while(!displayList.isSelectionEmpty())
465         {
466           final String hideKey = (String)displayList.getSelectedValue();
467 
468           hideFeatures.add(hideKey);
469           if(showFeatures.contains(hideKey))
470             showFeatures.remove(hideKey);
471 
472           sort(hideFeatures);
473           hideListModel.add(hideFeatures.indexOf(hideKey), hideKey);
474           showListModel.removeElement(hideKey);
475         }
476       }
477     });
478 
479     Box bdown = Box.createVerticalBox();
480     bdown.add(new JLabel("Features Displayed:"));
481     bdown.add(new JScrollPane(displayList));
482     bdown.add(hide_butt);
483 
484     final JPanel hideShowPanel = new JPanel(new BorderLayout());
485     hideShowPanel.add(bdown, BorderLayout.CENTER);
486 
487 
488     final JButton show_butt = new JButton("SHOW");
489     show_butt.addActionListener(new ActionListener()
490     {
491       public void actionPerformed(ActionEvent e)
492       {
493         while(!hideList.isSelectionEmpty())
494         {
495           final String showKey = (String)hideList.getSelectedValue();
496 
497           if(hideFeatures.contains(showKey))
498             hideFeatures.remove(showKey);
499           showFeatures.add(showKey);
500           sort(showFeatures);
501 
502           showListModel.add(showFeatures.indexOf(showKey), showKey);
503           hideListModel.removeElement(showKey);
504         }
505       }
506     });
507 
508     bdown = Box.createVerticalBox();
509     bdown.add(Box.createVerticalGlue());
510     bdown.add(new JLabel("Features Hidden:"));
511     bdown.add(new JScrollPane(hideList));
512     bdown.add(show_butt);
513     hideShowPanel.add(bdown, BorderLayout.EAST);
514 
515     hideShowPanel.add(showObsolete, BorderLayout.SOUTH);
516 
517     int select = JOptionPane.showConfirmDialog(null, hideShowPanel,
518                             "Gene Model Features Displayed...",
519                              JOptionPane.OK_CANCEL_OPTION,
520                              JOptionPane.QUESTION_MESSAGE);
521 
522     if(select == JOptionPane.CANCEL_OPTION)
523       return;
524 
525     showHideGeneFeatures(features);
526   }
527 
528   /**
529    * Based on the hidenFeatures and showFeatures set the GFFStreamFeatures
530    * visibility
531    * @param features
532    */
showHideGeneFeatures(final FeatureVector features)533   public static void showHideGeneFeatures(final FeatureVector features)
534   {
535     for(int i=0; i<features.size(); i++)
536     {
537       final Feature feature = features.elementAt(i).getEmblFeature();
538 
539       if(feature instanceof GFFStreamFeature)
540       {
541         if(isObsolete((GFFStreamFeature)feature))
542         {
543           if(!showObsolete.isSelected())
544           {
545             ((GFFStreamFeature)feature).setVisible(false);
546             continue;
547           }
548         }
549 
550         final String key = feature.getKey().getKeyString();
551         if(hideFeatures.contains(key))
552           ((GFFStreamFeature)feature).setVisible(false);
553         else
554           ((GFFStreamFeature)feature).setVisible(true);
555       }
556     }
557   }
558 
559   /**
560    * Determine based on the feature given if a feature is hidden
561    * @param key
562    * @return
563    */
isHiddenFeature(final String key)564   public static boolean isHiddenFeature(final String key)
565   {
566     return hideFeatures.contains(key);
567   }
568 
569   /**
570    * Test if this feature is obsolete
571    * @param feature
572    * @return
573    */
isObsolete(final uk.ac.sanger.artemis.io.GFFStreamFeature feature)574   public static boolean isObsolete(final uk.ac.sanger.artemis.io.GFFStreamFeature feature)
575   {
576     Qualifier qualifier = feature.getQualifierByName("isObsolete");
577     if(qualifier == null)
578       return false;
579 
580     if( ((String)qualifier.getValues().get(0)).equals("true") )
581       return true;
582 
583     return false;
584   }
585 
586   /**
587    *
588    */
duplicateGeneModel(final JFrame frame, final FeatureVector features_to_duplicate, final EntryGroup entry_group)589   public static Vector<ChadoCanonicalGene> duplicateGeneModel(final JFrame frame,
590       final FeatureVector features_to_duplicate,
591       final EntryGroup entry_group)
592   {
593     final Vector<ChadoCanonicalGene> duplicatedGenes = new Vector<ChadoCanonicalGene>();
594     final Vector<ChadoCanonicalGene> newGenes = new Vector<ChadoCanonicalGene>();
595     for (int i = 0 ; i < features_to_duplicate.size () ; ++i)
596     {
597       final GFFStreamFeature gffFeature =
598            (GFFStreamFeature)features_to_duplicate.elementAt(i).getEmblFeature();
599       if(duplicatedGenes.contains(gffFeature.getChadoGene()))
600         continue;
601 
602       duplicatedGenes.add(gffFeature.getChadoGene());
603 
604       try
605       {
606         GFFStreamFeature gene = (GFFStreamFeature)gffFeature.getChadoGene().getGene();
607         uk.ac.sanger.artemis.Feature newGeneFeature = ((uk.ac.sanger.artemis.Feature)
608             gene.getUserData()).duplicate (true);
609 
610         final ChadoCanonicalGene chadoGene = gffFeature.getChadoGene();
611         final ChadoCanonicalGene newchadoGene = new ChadoCanonicalGene();
612         newGenes.add(newchadoGene);
613         ((GFFStreamFeature)newGeneFeature.getEmblFeature()).setChadoGene(newchadoGene);
614         newchadoGene.setGene(newGeneFeature.getEmblFeature());
615 
616         final List<Feature> transcripts = chadoGene.getTranscripts();
617         for(int j=0; j<transcripts.size(); j++) // duplicate transcripts and children
618         {
619           final GFFStreamFeature transcript = (GFFStreamFeature)transcripts.get(j);
620           final String transcriptName = getUniqueName(transcript);
621 
622           uk.ac.sanger.artemis.Feature newTranscriptFeature = duplicateFeature(transcript, newchadoGene);
623           newchadoGene.addTranscript(newTranscriptFeature.getEmblFeature());
624           final String newTranscriptName = getUniqueName(newTranscriptFeature.getEmblFeature());
625 
626           List<uk.ac.sanger.artemis.Feature> newFeatures=
627               duplicateFeatures(chadoGene.get3UtrOfTranscript(transcriptName), newchadoGene);
628           for(uk.ac.sanger.artemis.Feature utrFeature: newFeatures)
629             newchadoGene.add3PrimeUtr(newTranscriptName, utrFeature.getEmblFeature());
630 
631           newFeatures = duplicateFeatures(chadoGene.get5UtrOfTranscript(transcriptName), newchadoGene);
632           for(uk.ac.sanger.artemis.Feature utrFeature: newFeatures)
633             newchadoGene.add5PrimeUtr(newTranscriptName, utrFeature.getEmblFeature());
634 
635           newFeatures = duplicateFeatures(chadoGene.getOtherFeaturesOfTranscript(transcriptName), newchadoGene);
636           for(uk.ac.sanger.artemis.Feature otherFeature: newFeatures)
637             newchadoGene.addOtherFeatures(newTranscriptName, otherFeature.getEmblFeature());
638 
639           newFeatures = duplicateFeatures(chadoGene.getSplicedFeaturesOfTranscript(transcriptName), newchadoGene);
640           for(uk.ac.sanger.artemis.Feature splicedFeature: newFeatures)
641             newchadoGene.addSplicedFeatures(newTranscriptName, splicedFeature.getEmblFeature());
642 
643           uk.ac.sanger.artemis.Feature newProtein =
644             duplicateFeature(chadoGene.getProteinOfTranscript(transcriptName), newchadoGene);
645           if(newProtein != null)
646             newchadoGene.addProtein(newTranscriptName, newProtein.getEmblFeature());
647         }
648       }
649       catch (ReadOnlyException e) {}
650     }
651 
652     duplicatedGenes.clear();
653     return newGenes;
654   }
655 
656 
duplicateFeatures( final List<Feature> featuresOfTranscript, final ChadoCanonicalGene chadoGene)657   private static List<uk.ac.sanger.artemis.Feature> duplicateFeatures(
658                                  final List<Feature> featuresOfTranscript,
659                                  final ChadoCanonicalGene chadoGene)
660           throws ReadOnlyException
661   {
662     final List<uk.ac.sanger.artemis.Feature> newFeatures =
663         new Vector<uk.ac.sanger.artemis.Feature>();
664 
665     if(featuresOfTranscript == null)
666       return newFeatures;
667 
668     for(int i=0; i<featuresOfTranscript.size(); i++)
669       newFeatures.add(duplicateFeature(
670           (GFFStreamFeature)featuresOfTranscript.get(i), chadoGene));
671     return newFeatures;
672   }
673 
duplicateFeature( final Feature feature, final ChadoCanonicalGene chadoGene)674   private static uk.ac.sanger.artemis.Feature duplicateFeature(
675           final Feature feature, final ChadoCanonicalGene chadoGene)
676           throws ReadOnlyException
677   {
678     if(feature == null)
679       return null;
680     uk.ac.sanger.artemis.Feature newFeature =
681       ((uk.ac.sanger.artemis.Feature)feature.getUserData()).duplicate(true);
682     ((GFFStreamFeature)newFeature.getEmblFeature()).setChadoGene(chadoGene);
683     if(isHiddenFeature(newFeature.getKey().getKeyString()))
684       ((GFFStreamFeature)newFeature.getEmblFeature()).setVisible(false);
685     return newFeature;
686   }
687 
688   /**
689    * Create gene model from base selection
690    * @param frame
691    * @param selection
692    * @param entry_group
693    * @param goto_event_source
694    */
createGeneModel(final JFrame frame, final Selection selection, final EntryGroup entry_group, final GotoEventSource goto_event_source)695   public static void createGeneModel(final JFrame frame,
696       final Selection selection,
697       final EntryGroup entry_group,
698       final GotoEventSource goto_event_source)
699   {
700     if(!SelectionMenu.checkForSelectionRange(frame, selection))
701       return;
702     final MarkerRange range = selection.getMarkerRange ();
703     final Entry default_entry = entry_group.getDefaultEntry ();
704 
705     if (default_entry == null)
706     {
707       new MessageDialog (frame, "There is no default entry");
708       return;
709     }
710 
711     QualifierVector qualifiers = new QualifierVector();
712     final String uniquename = promptForUniquename(entry_group,
713                                    range.isForwardMarker());
714     final Qualifier qualifier = new Qualifier("ID", uniquename);
715     qualifiers.add(qualifier);
716 
717     try
718     {
719       final FeatureVector newFeatures = new FeatureVector();
720       final Location new_location = range.createLocation ();
721       final Key key = new Key("gene");
722       final uk.ac.sanger.artemis.Feature geneFeature =
723           default_entry.createFeature(key, new_location, qualifiers);
724       newFeatures.add(geneFeature);
725 
726       final ChadoCanonicalGene chadoGene = new ChadoCanonicalGene();
727       chadoGene.setGene(geneFeature.getEmblFeature());
728       ((uk.ac.sanger.artemis.io.GFFStreamFeature)
729           (geneFeature.getEmblFeature())).setChadoGene(chadoGene);
730 
731       // create transcript
732       uk.ac.sanger.artemis.Feature transcript =
733         GeneViewerPanel.createTranscript(chadoGene, entry_group);
734       newFeatures.add(transcript);
735       ((uk.ac.sanger.artemis.io.GFFStreamFeature)
736           (transcript.getEmblFeature())).setChadoGene(chadoGene);
737       final String transcriptId =
738          (String)transcript.getQualifierByName("ID").getValues().get(0);
739 
740       // add exon
741       GFFStreamFeature exonFeature =
742          GeneViewerPanel.addExonFeature(chadoGene, entry_group,
743           null, new_location.getTotalRange(), transcriptId, selection,
744           new Key(DatabaseDocument.EXONMODEL), null);
745 
746       // add protein
747       uk.ac.sanger.artemis.Feature polypep =
748         GeneViewerPanel.addProteinFeature(chadoGene, entry_group, transcriptId, transcript);
749       newFeatures.add(polypep);
750 
751       // add inferred CDS
752       if(DatabaseDocument.CHADO_INFER_CDS)
753         DatabaseInferredFeature.createFeature(transcriptId, exonFeature,
754                               chadoGene, entry_group.getDefaultEntry());
755 
756       showHideGeneFeatures(newFeatures);
757       selection.clear();
758       selection.add(polypep);
759 
760       EditMenu.editSelectedFeatures(entry_group, selection,
761           goto_event_source, polypep, null, null);
762     }
763     catch(ReadOnlyException e)
764     {
765       // TODO Auto-generated catch block
766       e.printStackTrace();
767     }
768     catch(EntryInformationException e)
769     {
770       // TODO Auto-generated catch block
771       e.printStackTrace();
772     }
773     catch(OutOfRangeException e)
774     {
775       // TODO Auto-generated catch block
776       e.printStackTrace();
777     }
778   }
779 
780   /**
781    * Prompt the user for an ID
782    * @return
783    */
promptForUniquename(final EntryGroup entry_group, final boolean is_forward)784   public static String promptForUniquename(final EntryGroup entry_group,
785                                             final boolean is_forward)
786   {
787     final Entry default_entry = entry_group.getDefaultEntry ();
788     String id = null;
789 
790     if(default_entry.getEMBLEntry() instanceof
791         uk.ac.sanger.artemis.io.DatabaseDocumentEntry ||
792        default_entry.getEMBLEntry() instanceof
793         uk.ac.sanger.artemis.io.GFFDocumentEntry)
794     {
795       while(id == null ||
796             id.equals("") ||
797             id.equals("to_be_set"))
798       {
799         String msg = "Provide a unique ID ";
800 
801         if(!is_forward)
802           msg = msg + "for reverse strand : ";
803         else
804           msg = msg + ": ";
805 
806         id = JOptionPane.showInputDialog(null,
807                            msg,
808                            "ID missing ",
809                            JOptionPane.QUESTION_MESSAGE).trim();
810 
811         if(!isUniqueID(entry_group, id))
812         {
813           JOptionPane.showMessageDialog(null,
814               "ID "+id+" not unique.\nEnter a unique ID.",
815               "ID Not Unique",
816               JOptionPane.WARNING_MESSAGE);
817           id = null;
818         }
819       }
820     }
821     return id;
822   }
823 
824   /**
825    * Prompt the user for an ID and provide a default automated ID
826    * based on the range.
827    * @param entry_group
828    * @param is_forward
829    * @param range
830    * @return
831    */
promptForUniquename(final EntryGroup entry_group, final boolean is_forward, final Range range)832   public static String promptForUniquename(final EntryGroup entry_group,
833                                            final boolean is_forward,
834                                            final Range range)
835   {
836     final Entry default_entry = entry_group.getDefaultEntry ();
837     String id = null;
838 
839     if(default_entry.getEMBLEntry() instanceof
840         uk.ac.sanger.artemis.io.DatabaseDocumentEntry)
841     {
842 
843       while(id == null ||
844             id.equals("") ||
845             id.equals("to_be_set"))
846       {
847         String msg = "Provide a unique ID ";
848 
849         if(!is_forward)
850           msg = msg + "for reverse strand : ";
851         else
852           msg = msg + ": ";
853 
854 
855         id = JOptionPane.showInputDialog(null, msg,
856             default_entry.getName()+":"+
857             range.getStart()+".."+
858             range.getEnd());
859 
860         if(!isUniqueID(entry_group, id))
861         {
862           JOptionPane.showMessageDialog(null,
863               "ID "+id+" not unique.\nEnter a unique ID.",
864               "ID Not Unique",
865               JOptionPane.WARNING_MESSAGE);
866           id = null;
867         }
868       }
869     }
870     return id;
871   }
872 
873 
874   /**
875    * Test to ensure ID (chado uniquename) is unique.
876    * @param entry_group
877    * @param id
878    * @return
879    */
isUniqueID(final EntryGroup entry_group, final String id)880   private static boolean isUniqueID(final EntryGroup entry_group,
881                                     final String id)
882   {
883     final FeaturePredicate predicate =
884       new FeatureKeyQualifierPredicate(null, "ID", id,
885                                        false, true);
886     final FeatureVector features = entry_group.getAllFeatures();
887     for(int i=0; i<features.size(); i++)
888     {
889       uk.ac.sanger.artemis.Feature feature = features.elementAt(i);
890       if(predicate.testPredicate(feature))
891         return false;
892 
893     }
894     return true;
895   }
896 
897   /**
898    * Given an group of entries determine if they contain a database entry
899    * @param entryGroup
900    * @return
901    */
isDatabaseEntry(final EntryGroup entryGroup)902   public static boolean isDatabaseEntry(final EntryGroup entryGroup)
903   {
904     final EntryVector entries = entryGroup.getActiveEntries();
905 
906     for(int i=0; i<entries.size(); i++)
907     {
908       if( entries.elementAt(i).getEMBLEntry() instanceof DatabaseDocumentEntry )
909         return true;
910     }
911     return false;
912   }
913 
914   /**
915    * Given an group of entries determine if they contain a GFF entry
916    * @param entryGroup
917    * @return
918    */
isGFFEntry(final EntryGroup entryGroup)919   public static boolean isGFFEntry(final EntryGroup entryGroup)
920   {
921     final EntryVector entries = entryGroup.getActiveEntries();
922 
923     for(int i=0; i<entries.size(); i++)
924     {
925       if( entries.elementAt(i).getEMBLEntry() instanceof GFFDocumentEntry )
926         return true;
927     }
928     return false;
929   }
930 
931 
932   /**
933    * Given a feature determine if it belongs to a database entry
934    * @param entryGroup
935    * @return
936    */
isDatabaseEntry(final Feature feature)937   public static boolean isDatabaseEntry(final Feature feature)
938   {
939     if( feature.getEntry() instanceof DatabaseDocumentEntry &&
940         ((GFFStreamFeature)feature).getDocumentEntry().getDocument() instanceof DatabaseDocument)
941       return true;
942 
943     return false;
944   }
945 
946 
deleteFeature(uk.ac.sanger.artemis.Feature feature)947   private static void deleteFeature(uk.ac.sanger.artemis.Feature feature)
948       throws ReadOnlyException
949   {
950     if(feature != null && feature.getEntry() != null)
951       feature.removeFromEntry();
952   }
953 
deleteAllFeature(uk.ac.sanger.artemis.Feature feature, final ChadoCanonicalGene chado_gene)954   public static void deleteAllFeature(uk.ac.sanger.artemis.Feature feature,
955       final ChadoCanonicalGene chado_gene) throws ReadOnlyException
956   {
957     deleteAllFeature(feature, chado_gene, true);
958   }
959 
960   /**
961    * Delete feature and children in a chado gene model
962    * @param feature
963    * @param chado_gene
964    * @throws ReadOnlyException
965    */
deleteAllFeature(uk.ac.sanger.artemis.Feature feature, final ChadoCanonicalGene chado_gene, final boolean updateChadoCanonicalGene)966   public static void deleteAllFeature(uk.ac.sanger.artemis.Feature feature,
967       final ChadoCanonicalGene chado_gene, final boolean updateChadoCanonicalGene) throws ReadOnlyException
968   {
969     Set<Feature> children = chado_gene.getChildren(feature.getEmblFeature());
970     deleteFeature(feature);
971     if(updateChadoCanonicalGene)
972       chado_gene.deleteFeature(feature.getEmblFeature());
973 
974     Feature embl_feature;
975     Iterator<Feature> it = children.iterator();
976 
977     while(it.hasNext())
978     {
979       embl_feature = it.next();
980       deleteFeature((uk.ac.sanger.artemis.Feature) embl_feature.getUserData());
981       if(updateChadoCanonicalGene)
982         chado_gene.deleteFeature(embl_feature);
983     }
984   }
985 
986   /**
987    * Check gene model strands for any inconsistencies
988    * @param chado_gene
989    * @return true is gene model ranges are correct
990    */
isStrandOK(final ChadoCanonicalGene chado_gene)991   public static boolean isStrandOK(final ChadoCanonicalGene chado_gene)
992   {
993     boolean isRev = chado_gene.getGene().getLocation().isComplement();
994     final List<Feature> transcripts = chado_gene.getTranscripts();
995 
996     for(int i=0; i<transcripts.size(); i++)
997     {
998       final Feature transcript = (Feature)transcripts.get(i);
999 
1000       if(isRev ^ transcript.getLocation().isComplement())
1001         return false;
1002 
1003       final Feature protein =
1004         chado_gene.getProteinOfTranscript(GeneUtils.getUniqueName(transcript));
1005       if(protein != null && (isRev ^ protein.getLocation().isComplement()))
1006         return false;
1007 
1008       final Set<Feature> children = chado_gene.getChildren(transcript);
1009       final Iterator<Feature> it = children.iterator();
1010       while(it.hasNext())
1011       {
1012         final Feature feature = it.next();
1013         if(isRev ^ feature.getLocation().isComplement())
1014           return false;
1015       }
1016     }
1017     return true;
1018   }
1019 
1020   /**
1021    * Check gene model boundaries for any inconsistencies
1022    * @param chado_gene
1023    * @return 0 - if consisent
1024    *         1 - if transcript start or end is outside gene range
1025    *         2 - if child feature of a transcript is outside the transcript range
1026    *         3 - if the span of the children features does not match start and end of the transcript
1027    *         4 - if the protein range does not match CDS
1028    *         5 - if the gene range does not match the largest transcript range
1029    */
isBoundaryOK(final ChadoCanonicalGene chado_gene)1030   public static int isBoundaryOK(final ChadoCanonicalGene chado_gene)
1031   {
1032     final Range geneRange = chado_gene.getGene().getLocation().getTotalRange();
1033     final List<Feature> transcripts = chado_gene.getTranscripts();
1034     int geneStart = Integer.MAX_VALUE;
1035     int geneEnd = -1;
1036 
1037     for(Feature transcript: transcripts)
1038     {
1039       final Range transcriptRange = transcript.getLocation().getTotalRange();
1040       int transcriptStart = Integer.MAX_VALUE;
1041       int transcriptEnd = -1;
1042       int ppStart = Integer.MAX_VALUE;
1043       int ppEnd   = -1;
1044 
1045       if(transcriptRange.getStart() < geneRange.getStart() ||
1046          transcriptRange.getEnd()   > geneRange.getEnd())
1047         return 1;
1048 
1049       if(transcriptRange.getStart() < geneStart)
1050         geneStart = transcriptRange.getStart();
1051       if(transcriptRange.getEnd() > geneEnd)
1052         geneEnd = transcriptRange.getEnd();
1053 
1054       final Feature protein =
1055         chado_gene.getProteinOfTranscript(GeneUtils.getUniqueName(transcript));
1056       String proteinName = null;
1057       if(protein != null)
1058         proteinName = GeneUtils.getUniqueName(protein);
1059 
1060       final Set<Feature> children = chado_gene.getChildren(transcript);
1061       final Iterator<Feature> it = children.iterator();
1062       while(it.hasNext())
1063       {
1064         final Feature feature = it.next();
1065         final Range childRange = feature.getLocation().getTotalRange();
1066         if(childRange.getStart() < transcriptRange.getStart() ||
1067            childRange.getEnd()   > transcriptRange.getEnd())
1068           return 2;
1069 
1070         if(proteinName != null &&
1071            GeneUtils.getUniqueName(feature).equals(proteinName))
1072           continue;
1073 
1074         if(childRange.getStart() < transcriptStart)
1075           transcriptStart = childRange.getStart();
1076         if(childRange.getEnd() > transcriptEnd )
1077           transcriptEnd = childRange.getEnd();
1078 
1079         String keyStr = feature.getKey().getKeyString();
1080         if( (DatabaseDocument.CHADO_INFER_CDS  && keyStr.equals("CDS")) ||
1081             (!DatabaseDocument.CHADO_INFER_CDS && keyStr.equals(DatabaseDocument.EXONMODEL)) ||
1082            feature.getKey().equals("pseudogenic_exon"))
1083         {
1084           if(childRange.getStart() < ppStart)
1085             ppStart = childRange.getStart();
1086           if(childRange.getEnd() > ppEnd )
1087             ppEnd   = childRange.getEnd();
1088         }
1089       }
1090 
1091       if((transcriptRange.getStart() != transcriptStart && transcriptStart < Integer.MAX_VALUE) ||
1092          (transcriptRange.getEnd()   != transcriptEnd   && transcriptEnd   > -1))
1093         return 3;
1094 
1095       if(protein != null)
1096       {
1097         final Range proteinRange = protein.getLocation().getTotalRange();
1098         if((proteinRange.getStart() != ppStart && ppStart < Integer.MAX_VALUE) ||
1099            (proteinRange.getEnd()   != ppEnd   && ppEnd   > -1))
1100           return 4;
1101       }
1102     }
1103 
1104     // check gene range
1105     if((geneRange.getStart() != geneStart && geneStart < Integer.MAX_VALUE) ||
1106        (geneRange.getEnd()   != geneEnd   && geneEnd   > -1))
1107       return 5;
1108 
1109     return 0;
1110   }
1111 
checkGeneBoundary(final ChadoCanonicalGene chado_gene)1112   public static void checkGeneBoundary(final ChadoCanonicalGene chado_gene)
1113   {
1114     checkGeneBoundary(chado_gene, true);
1115   }
1116 
checkTranscriptBoundary( final uk.ac.sanger.artemis.Feature transcript, final ChadoCanonicalGene chado_gene)1117   protected static Range checkTranscriptBoundary(
1118       final uk.ac.sanger.artemis.Feature transcript,
1119       final ChadoCanonicalGene chado_gene)
1120   {
1121     return checkTranscriptBoundary(transcript, chado_gene, true);
1122   }
1123 
1124   /**
1125    * Adjust transcript and gene boundaries
1126    * @param chado_gene
1127    */
checkGeneBoundary(final ChadoCanonicalGene chado_gene, final boolean changeEmblFeature)1128   public static void checkGeneBoundary(final ChadoCanonicalGene chado_gene, final boolean changeEmblFeature)
1129   {
1130     final List<Feature> transcripts = chado_gene.getTranscripts();
1131     int gene_start = Integer.MAX_VALUE;
1132     int gene_end = -1;
1133 
1134     Range range;
1135     for(Feature transcript: transcripts)
1136     {
1137       range = checkTranscriptBoundary(
1138           (uk.ac.sanger.artemis.Feature)transcript.getUserData(), chado_gene, changeEmblFeature);
1139       if(range != null && range.getStart() < gene_start)
1140         gene_start = range.getStart();
1141       if(range != null && range.getEnd() > gene_end)
1142         gene_end = range.getEnd();
1143     }
1144 
1145     if(gene_end == -1 && gene_start == Integer.MAX_VALUE)
1146       return;
1147 
1148     setLocation(chado_gene.getGene(), gene_start, gene_end, changeEmblFeature);
1149   }
1150 
1151   /**
1152    * Check and adjust transcript boundary
1153    * @param transcript
1154    * @param chado_gene
1155    * @param changeEmblFeature
1156    * @return
1157    */
checkTranscriptBoundary( final uk.ac.sanger.artemis.Feature transcript, final ChadoCanonicalGene chado_gene, final boolean changeEmblFeature)1158   protected static Range checkTranscriptBoundary(
1159       final uk.ac.sanger.artemis.Feature transcript,
1160       final ChadoCanonicalGene chado_gene,
1161       final boolean changeEmblFeature)
1162   {
1163     final List transcripts = chado_gene.getTranscripts();
1164 
1165     if(transcripts.contains(transcript.getEmblFeature()))
1166     {
1167       checkProteinBoundary(transcript.getEmblFeature(), chado_gene, changeEmblFeature);
1168 
1169       final Set children = chado_gene.getChildren(transcript.getEmblFeature());
1170       int transcript_start = Integer.MAX_VALUE;
1171       int transcript_end = -1;
1172 
1173       final Iterator it = children.iterator();
1174       while(it.hasNext())
1175       {
1176         final Feature feature = (Feature) it.next();
1177         final Range range = feature.getLocation().getTotalRange();
1178         if(range.getStart() < transcript_start)
1179           transcript_start = range.getStart();
1180         if(range.getEnd() > transcript_end)
1181           transcript_end = range.getEnd();
1182       }
1183 
1184       if(transcript_start == Integer.MAX_VALUE ||
1185          transcript_end == -1)
1186         return null;
1187 
1188       return setLocation(transcript.getEmblFeature(),
1189             transcript_start, transcript_end, changeEmblFeature);
1190     }
1191     else
1192       JOptionPane.showMessageDialog(null,
1193           "Select a single transcript and try again.", "Transcript Selection",
1194           JOptionPane.ERROR_MESSAGE);
1195     return null;
1196   }
1197 
1198   /**
1199    * Check and adjust protein boundary
1200    * @param transcript
1201    * @param chado_gene
1202    */
checkProteinBoundary(final Feature transcript, final ChadoCanonicalGene chado_gene, final boolean changeEmblFeature)1203   private static void checkProteinBoundary(final Feature transcript,
1204                                           final ChadoCanonicalGene chado_gene,
1205                                           final boolean changeEmblFeature)
1206   {
1207     final String transcriptName = getUniqueName(transcript);
1208     final Feature protein = chado_gene.getProteinOfTranscript(transcriptName);
1209     if(protein == null)
1210       return;
1211 
1212     int pp_start = Integer.MAX_VALUE;
1213     int pp_end = -1;
1214 
1215     final List<Feature> dnaFeatures = new Vector<Feature>();
1216 /*    if(chado_gene.get3UtrOfTranscript(transcriptName) != null)
1217       dnaFeatures.addAll(chado_gene.get3UtrOfTranscript(transcriptName));
1218     if(chado_gene.get5UtrOfTranscript(transcriptName) != null)
1219       dnaFeatures.addAll(chado_gene.get5UtrOfTranscript(transcriptName));*/
1220 
1221     List<Feature> exons;
1222     if(DatabaseDocument.CHADO_INFER_CDS)
1223       exons = chado_gene.getSpliceSitesOfTranscript(transcriptName, "CDS");
1224     else
1225       exons = chado_gene.getSpliceSitesOfTranscript(transcriptName, DatabaseDocument.EXONMODEL);
1226     if(exons != null)
1227       dnaFeatures.addAll(exons);
1228 
1229     exons = chado_gene.getSpliceSitesOfTranscript(transcriptName, "pseudogenic_exon");
1230     if(exons != null)
1231       dnaFeatures.addAll(exons);
1232 
1233     for(Feature dnaFeature: dnaFeatures)
1234     {
1235       final Range range = dnaFeature.getLocation().getTotalRange();
1236       if(range.getStart() < pp_start)
1237         pp_start = range.getStart();
1238       if(range.getEnd() > pp_end)
1239         pp_end = range.getEnd();
1240     }
1241 
1242     if(pp_start == Integer.MAX_VALUE || pp_end == -1)
1243        return;
1244 
1245     setLocation(protein, pp_start, pp_end, changeEmblFeature);
1246   }
1247 
1248   /**
1249    * For a feature propagate the uniquename as the prefix for
1250    * the associated children.
1251    * @param gene
1252    * @param newName
1253    * @param children
1254    */
propagateId(final GFFStreamFeature feature, final String newName, final Set children)1255   public static void propagateId(final GFFStreamFeature feature,
1256                                  final String newName,
1257                                  final Set children)
1258   {
1259     final ChadoCanonicalGene gene = feature.getChadoGene();
1260     final Iterator it = children.iterator();
1261     while(it.hasNext())
1262     {
1263       final GFFStreamFeature child = (GFFStreamFeature)it.next();
1264       final Hashtable segmentHash  = child.getSegmentRangeStore();
1265 
1266       final String oldId = getUniqueName(child);
1267 
1268       final Set childrenOfChild = gene.getChildren(child);
1269       int index = oldId.lastIndexOf('.');
1270 
1271       if(index == -1)
1272         index = oldId.indexOf(':');
1273 
1274       if(index > -1)
1275       {
1276         final String newId;
1277         if(  segmentHash != null &&
1278            ( segmentHash.size() > 1 ||
1279              child.getKey().getKeyString().equals(DatabaseDocument.EXONMODEL)))
1280         {
1281           final Set idKeys = segmentHash.keySet();
1282           final Hashtable newSegmentHash = new Hashtable(idKeys.size());
1283           final Iterator itKeys = idKeys.iterator();
1284           final Hashtable newIdMapToOldId = new Hashtable(idKeys.size());
1285           while(itKeys.hasNext())
1286           {
1287             String oldKey = (String)itKeys.next();
1288             index = oldKey.lastIndexOf('.');
1289             if(index == -1)
1290               index = oldKey.indexOf(':');
1291             final String newKey = newName + oldKey.substring(index);
1292             Object range = segmentHash.get(oldKey);
1293 
1294             newSegmentHash.put(newKey, range);
1295             newIdMapToOldId.put(newKey, oldKey);
1296           }
1297           child.setSegmentRangeStore(newSegmentHash);
1298           child.setNewIdMapToOldId(newIdMapToOldId);
1299           newId = child.getSegmentID(child.getLocation().getRanges());
1300         }
1301         else
1302           newId = newName + oldId.substring(index);
1303         try
1304         {
1305           ((uk.ac.sanger.artemis.Feature) child.getUserData())
1306               .setQualifier(new Qualifier("ID", newId));
1307           gene.updateUniqueName(oldId, newId, childrenOfChild);
1308         }
1309         catch(ReadOnlyException e)
1310         {
1311           e.printStackTrace();
1312         }
1313         catch(EntryInformationException e)
1314         {
1315           e.printStackTrace();
1316         }
1317       }
1318     }
1319   }
1320 
1321   /**
1322    * Fix parent (Parent and Derives_from) qualifiers. Used when the
1323    * uniqueName of the parent has been changed.
1324    * @param oldName
1325    * @param newName
1326    * @param children
1327    */
fixParentQualifier(final String oldName, final String newName, final Set children)1328   public static void fixParentQualifier(final String oldName,
1329                                         final String newName,
1330                                         final Set children)
1331   {
1332     final Iterator it = children.iterator();
1333     while(it.hasNext())
1334     {
1335       Feature child = (Feature)it.next();
1336       QualifierVector qualifiers = child.getQualifiers();
1337       if( qualifiers.getQualifierByName("Parent") != null &&
1338           ((String)(qualifiers.getQualifierByName("Parent").getValues().get(0))).equals(oldName) )
1339       {
1340         qualifiers.removeQualifierByName("Parent");
1341         qualifiers.setQualifier(new Qualifier("Parent", newName));
1342       }
1343       else if( qualifiers.getQualifierByName("Derives_from") != null &&
1344           ((String)(qualifiers.getQualifierByName("Derives_from").getValues().get(0))).equals(oldName) )
1345       {
1346         qualifiers.removeQualifierByName("Derives_from");
1347         qualifiers.setQualifier(new Qualifier("Derives_from", newName));
1348       }
1349     }
1350   }
1351 
1352   /**
1353    * Converts a gene to pseudogene or vice-versa
1354    * @param chado_gene
1355    * @throws EntryInformationException
1356    * @throws ReadOnlyException
1357    * @throws OutOfRangeException
1358    */
convertPseudo(final ChadoCanonicalGene chado_gene)1359   public static void convertPseudo(final ChadoCanonicalGene chado_gene)
1360          throws ReadOnlyException, EntryInformationException, OutOfRangeException
1361   {
1362     final Key geneKey = chado_gene.getGene().getKey();
1363 
1364     boolean convertToPseudogene = false;
1365 
1366     uk.ac.sanger.artemis.Feature gene = (uk.ac.sanger.artemis.Feature)chado_gene.getGene().getUserData();
1367     if(geneKey.equals("gene"))
1368     {
1369       gene.set(new Key("pseudogene"), gene.getLocation(), gene.getQualifiers());
1370       convertToPseudogene = true;
1371     }
1372     else if(geneKey.equals("pseudogene"))
1373       gene.set(new Key("gene"), gene.getLocation(), gene.getQualifiers());
1374     else
1375       return;
1376 
1377     final List transcripts = chado_gene.getTranscripts();
1378     for(int i=0; i<transcripts.size(); i++)
1379     {
1380       final uk.ac.sanger.artemis.Feature transcript = (
1381           uk.ac.sanger.artemis.Feature)((Feature)transcripts.get(i)).getUserData();
1382       final String transcriptName = getUniqueName(transcript.getEmblFeature());
1383       final List exons;
1384       if(convertToPseudogene)
1385       {
1386         exons = chado_gene.getSpliceSitesOfTranscript(transcriptName,
1387                                          DatabaseDocument.EXONMODEL);
1388         transcript.set(new Key("pseudogenic_transcript"), transcript.getLocation(),
1389             transcript.getQualifiers());
1390       }
1391       else
1392       {
1393         exons = chado_gene.getSpliceSitesOfTranscript(transcriptName,
1394                                                  "pseudogenic_exon");
1395         transcript.set(new Key(DatabaseDocument.TRANSCRIPT), transcript.getLocation(), transcript.getQualifiers());
1396       }
1397 
1398       if(exons == null)
1399         continue;
1400 
1401       for(int j=0; j<exons.size(); j++)
1402       {
1403         final uk.ac.sanger.artemis.Feature exon = (uk.ac.sanger.artemis.Feature)((Feature)exons.get(j)).getUserData();
1404         exon.resetColour();
1405         if(convertToPseudogene)
1406           exon.set(new Key("pseudogenic_exon"), exon.getLocation(), exon.getQualifiers());
1407         else
1408           exon.set(new Key(DatabaseDocument.EXONMODEL), exon.getLocation(), exon.getQualifiers());
1409       }
1410     }
1411   }
1412 
setLocation(final Feature f, final int start, final int end, final boolean changeEmblFeature)1413   private static Range setLocation(final Feature f,
1414                                    final int start,
1415                                    final int end,
1416                                    final boolean changeEmblFeature)
1417   {
1418     try
1419     {
1420       final RangeVector ranges = new RangeVector();
1421       final Range range = new Range(start, end);
1422       ranges.add(range);
1423 
1424       final Location new_location = new Location(ranges,
1425           f.getLocation().isComplement());
1426 
1427       if(changeEmblFeature)
1428         f.setLocation(new_location);
1429       else
1430         ((uk.ac.sanger.artemis.Feature)f.getUserData()).setLocation(new_location);
1431       return range;
1432     }
1433     catch (OutOfRangeException e)
1434     {
1435       // TODO Auto-generated catch block
1436       e.printStackTrace();
1437     }
1438     catch (ReadOnlyException e)
1439     {
1440       // TODO Auto-generated catch block
1441       e.printStackTrace();
1442     }
1443     return null;
1444   }
1445 
getUniqueName(final Feature feature)1446   public static String getUniqueName(final Feature feature)
1447   {
1448     try
1449     {
1450       return (String)feature.getQualifierByName("ID").getValues().get(0);
1451     }
1452     catch(InvalidRelationException e)
1453     {
1454       e.printStackTrace();
1455     }
1456     return null;
1457   }
1458 
1459   /**
1460    * Return an array on non-coding transcripts
1461    * @return
1462    */
getNonCodingTranscripts()1463   public static String[] getNonCodingTranscripts()
1464   {
1465     return nonCodingTranscripts;
1466   }
1467 
1468   /**
1469    * Test if the key given is a non-coding transcript key
1470    * @param key
1471    * @return
1472    */
isNonCodingTranscripts(final Key key)1473   public static boolean isNonCodingTranscripts(final Key key)
1474   {
1475     for(int i=0; i<nonCodingTranscripts.length; i++)
1476       if(nonCodingTranscripts[i].equals(key.getKeyString()))
1477         return true;
1478     return false;
1479   }
1480 
deriveResidues(final GFFStreamFeature gffFeature)1481   public static String deriveResidues(final GFFStreamFeature gffFeature)
1482   {
1483     final ChadoCanonicalGene chadoGene = gffFeature.getChadoGene();
1484 
1485     boolean isProteinFeature =
1486       ((uk.ac.sanger.artemis.Feature)gffFeature.getUserData()).isProteinFeature();
1487 
1488     String residues = null;
1489     try
1490     {
1491       String transcriptName =
1492         chadoGene.getTranscriptFromName(GeneUtils.getUniqueName(gffFeature));
1493 
1494       List<Feature> splicedFeatures =
1495         chadoGene.getSplicedFeaturesOfTranscript(transcriptName);
1496       for (Feature emblFeature: splicedFeatures)
1497       {
1498         if (emblFeature.getKey().getKeyString().equals(
1499             DatabaseDocument.EXONMODEL) ||
1500             emblFeature.getKey().getKeyString().equals("pseudogenic_exon"))
1501         {
1502           uk.ac.sanger.artemis.Feature f =
1503             (uk.ac.sanger.artemis.Feature) emblFeature.getUserData();
1504           if (!isProteinFeature)
1505             residues = f.getTranslationBases();
1506           else
1507             residues = f.getTranslation().toString();
1508         }
1509       }
1510     }
1511     catch (Exception e){  }
1512 
1513     return residues;
1514   }
1515 
getFeatureForUpdatingResidues( final GFFStreamFeature gffFeature)1516   public static FeatureForUpdatingResidues getFeatureForUpdatingResidues(
1517       final GFFStreamFeature gffFeature)
1518   {
1519     if(!isFeatureToUpdateResidues(gffFeature.getKey().getKeyString()))
1520       return null;
1521     String residues = deriveResidues(gffFeature);
1522     if(residues == null)
1523       return null;
1524     final FeatureForUpdatingResidues chadoFeature = new FeatureForUpdatingResidues();
1525     chadoFeature.setStartBase(0);
1526     chadoFeature.setLength(residues.length());
1527     chadoFeature.setNewSubSequence(residues);
1528     chadoFeature.setResidueUpdate(true);
1529 
1530     if(gffFeature.getQualifierByName("feature_id") != null)
1531       chadoFeature.setFeatureId( Integer.parseInt( (String)
1532           gffFeature.getQualifierByName("feature_id").getValues().get(0)) );
1533     else
1534     {
1535       chadoFeature.setFeatureId(-1);
1536       chadoFeature.setUniqueName(getUniqueName(gffFeature));
1537     }
1538     chadoFeature.setSeqLen(residues.length());
1539     return chadoFeature;
1540   }
1541 
1542   /**
1543    * Look at the sequence_update_features property in the options
1544    * file to see if this is a feature to update residues for.
1545    * @param keyStr
1546    * @return
1547    */
isFeatureToUpdateResidues(final String keyStr)1548   public static boolean isFeatureToUpdateResidues(final String keyStr)
1549   {
1550     if(featuresToUpdateResidues == null)
1551       return false;
1552 
1553     return featuresToUpdateResidues.contains(keyStr);
1554   }
1555 
main(String args[])1556   public static void main(String args[])
1557   {
1558     GeneUtils.defineShowHideGeneFeatures(new FeatureVector());
1559   }
1560 }
1561