JAL-1705 return Ensembl genes for model species for a gene name
[jalview.git] / src / jalview / ext / ensembl / EnsemblGene.java
index 73649b4..10841bd 100644 (file)
@@ -7,8 +7,8 @@ import jalview.datamodel.SequenceI;
 import jalview.io.gff.SequenceOntologyFactory;
 import jalview.io.gff.SequenceOntologyI;
 import jalview.util.MapList;
+import jalview.util.StringUtils;
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -24,9 +24,11 @@ public class EnsemblGene extends EnsemblSeqProxy
 {
   private static final String GENE_PREFIX = "gene:";
 
-  // TODO modify to accept other species e.g. ENSMUSGnnn
-  private static final Regex ACCESSION_REGEX = new Regex(
-          "(ENSG|ENST)[0-9]{11}$");
+  /*
+   * accepts anything as we will attempt lookup of gene or 
+   * transcript id or gene name
+   */
+  private static final Regex ACCESSION_REGEX = new Regex(".*");
 
   private static final EnsemblFeatureType[] FEATURES_TO_FETCH = {
       EnsemblFeatureType.gene, EnsemblFeatureType.transcript,
@@ -52,8 +54,15 @@ public class EnsemblGene extends EnsemblSeqProxy
   }
 
   /**
-   * Builds an alignment of all transcripts for the requested gene:
+   * Returns an alignment containing the gene(s) for the given gene or
+   * transcript identifier, or external identifier (e.g. Uniprot id). If given a
+   * gene name or external identifier, returns any related gene sequences found
+   * for model organisms. If only a single gene is queried for, then its
+   * transcripts are also retrieved and added to the alignment. <br>
+   * Method:
    * <ul>
+   * <li>resolves a transcript identifier by looking up its parent gene id</li>
+   * <li>resolves an external identifier by looking up xref-ed gene ids</li>
    * <li>fetches the gene sequence</li>
    * <li>fetches features on the sequence</li>
    * <li>identifies "transcript" features whose Parent is the requested gene</li>
@@ -65,81 +74,81 @@ public class EnsemblGene extends EnsemblSeqProxy
    * <li>aligns each transcript against the gene sequence based on the position
    * mappings</li>
    * </ul>
+   * 
+   * @param query
+   *          one or more identifiers separated by a space
+   * @return an alignment containing one or more genes, and possibly
+   *         transcripts, or null
    */
   @Override
   public AlignmentI getSequenceRecords(String query) throws Exception
   {
-    List<String> transcriptsWanted = null;
+    // todo: tidy up handling of one or multiple accession ids
+    String[] queries = query.split(getAccessionSeparator());
 
+    /*
+     * if given a transcript id, look up its gene parent
+     */
     if (isTranscriptIdentifier(query))
     {
-      transcriptsWanted = Arrays.asList(query
-              .split(getAccessionSeparator()));
-      query = getGeneForTranscript(query);
+      // we are assuming all transcripts have the same gene parent here
+      query = new EnsemblLookup().getParent(queries[0]);
       if (query == null)
       {
         return null;
       }
     }
 
+    /*
+     * if given a gene or other external name, lookup and fetch 
+     * the corresponding gene for all model organisms 
+     */
+    if (!isGeneIdentifier(query))
+    {
+      List<String> geneIds = new EnsemblSymbol().getIds(query);
+      if (geneIds.isEmpty())
+      {
+        return null;
+      }
+      String theIds = StringUtils.listToDelimitedString(geneIds,
+              getAccessionSeparator());
+      return getSequenceRecords(theIds);
+    }
+
     AlignmentI al = super.getSequenceRecords(query);
-    if (al.getHeight() > 0)
+
+    /*
+     * if we retrieved a single gene, get its transcripts as well
+     */
+    if (al.getHeight() == 1)
     {
-      getTranscripts(al, query, transcriptsWanted);
+      getTranscripts(al, query);
     }
 
     return al;
   }
 
   /**
-   * Gets the parent gene identifier for a given transcript identifier, by
-   * retrieving 'transcript' features overlapping the transcript, and finding
-   * the Parent property of the feature whose id is the given identifier.
+   * Attempts to get Ensembl stable identifiers for model organisms for a gene
+   * name by calling the xrefs symbol REST service to resolve the gene name.
    * 
    * @param query
    * @return
    */
-  protected String getGeneForTranscript(String transcriptId)
+  protected String getGeneIdentifiersForName(String query)
   {
-    String geneId = null;
-
-    /*
-     * reduce multiple transcripts (e.g. from Uniprot x-ref) to the first
-     * one only as representative (they should all have the same gene)
-     */
-    transcriptId = transcriptId.split(getAccessionSeparator())[0];
-
-    try
+    List<String> ids = new EnsemblSymbol().getIds(query);
+    if (ids != null)
     {
-      EnsemblFeatureType[] geneFeature = new EnsemblFeatureType[] { EnsemblFeatureType.transcript };
-      AlignmentI al = new EnsemblFeatures().getSequenceRecords(
-              transcriptId, geneFeature);
-      if (al != null && al.getHeight() > 0)
+      for (String id : ids)
       {
-        SequenceFeature[] sfs = al.getSequenceAt(0).getSequenceFeatures();
-        if (sfs != null)
+        if (isGeneIdentifier(id))
         {
-          for (SequenceFeature sf : sfs)
-          {
-            if (transcriptId.equals(getTranscriptId(sf)))
-            {
-              String parent = (String) sf.getValue(PARENT);
-              if (parent != null && parent.startsWith(GENE_PREFIX))
-              {
-                geneId = parent.substring(5);
-              }
-              break;
-            }
-          }
+          return id;
         }
       }
-      return geneId;
-    } catch (IOException e)
-    {
-      System.err.println("Error retrieving gene id for " + transcriptId
-              + ": " + e.getMessage());
-      return null;
     }
+    return null;
   }
 
   /**
@@ -149,17 +158,14 @@ public class EnsemblGene extends EnsemblSeqProxy
    * 
    * @param al
    * @param accId
-   * @param transcriptsWanted
-   *          optional list of transcript ids to filter by
    * @throws Exception
    */
-  protected void getTranscripts(AlignmentI al, String accId,
-          List<String> transcriptsWanted)
+  protected void getTranscripts(AlignmentI al, String accId)
           throws Exception
   {
     SequenceI gene = al.getSequenceAt(0);
     List<SequenceFeature> transcriptFeatures = getTranscriptFeatures(accId,
-            gene, transcriptsWanted);
+            gene);
 
     for (SequenceFeature transcriptFeature : transcriptFeatures)
     {
@@ -250,6 +256,11 @@ public class EnsemblGene extends EnsemblSeqProxy
             transcript.getDatasetSequence(), mapping, parentId);
 
     /*
+     * fetch and save cross-references
+     */
+    super.getCrossReferences(transcript);
+
+    /*
      * and finally fetch the protein product and save as a cross-reference
      */
     new EnsemblCdna().addProteinProduct(transcript);
@@ -274,12 +285,10 @@ public class EnsemblGene extends EnsemblSeqProxy
    * 
    * @param accId
    * @param geneSequence
-   * @param transcriptsWanted
-   *          optional list of ids to filter on
    * @return
    */
   protected List<SequenceFeature> getTranscriptFeatures(String accId,
-          SequenceI geneSequence, List<String> transcriptsWanted)
+          SequenceI geneSequence)
   {
     List<SequenceFeature> transcriptFeatures = new ArrayList<SequenceFeature>();
 
@@ -292,14 +301,6 @@ public class EnsemblGene extends EnsemblSeqProxy
       {
         if (isTranscript(sf.getType()))
         {
-          if (transcriptsWanted != null)
-          {
-            String transcriptId = (String) sf.getValue("transcript_id");
-            if (!transcriptsWanted.contains(transcriptId))
-            {
-              // continue;
-            }
-          }
           String parent = (String) sf.getValue(PARENT);
           if (parentIdentifier.equals(parent))
           {