JAL-2210 two pass attempt to import retrieved sequences. First pass relies on matchin...
[jalview.git] / src / jalview / analysis / CrossRef.java
index 6779b87..71b0aa0 100644 (file)
@@ -421,19 +421,27 @@ public class CrossRef
 
     if (retrieved != null)
     {
-      updateDbrefMappings(seq, xrfs, retrieved, cf, fromDna);
+      boolean addedXref = false;
       for (SequenceI retrievedSequence : retrieved)
       {
         // dataset gets contaminated ccwith non-ds sequences. why ??!
         // try: Ensembl -> Nuc->Ensembl, Nuc->Uniprot-->Protein->EMBL->
         SequenceI retrievedDss = retrievedSequence.getDatasetSequence() == null ? retrievedSequence
                 : retrievedSequence.getDatasetSequence();
-        importCrossRefSeq(cf, dss, retrievedDss);
-        rseqs.add(retrievedDss);
-        if (dataset.findIndex(retrievedDss) == -1)
+        addedXref |= importCrossRefSeq(cf, dss, retrievedDss);
+      }
+      if (!addedXref)
+      {
+        // try again, after looking for matching IDs
+        // shouldn't need to do this unless the dbref mechanism has broken.
+        updateDbrefMappings(seq, xrfs, retrieved, cf, fromDna);
+        for (SequenceI retrievedSequence : retrieved)
         {
-          dataset.addSequence(retrievedDss);
-          matcher.add(retrievedDss);
+          // dataset gets contaminated ccwith non-ds sequences. why ??!
+          // try: Ensembl -> Nuc->Ensembl, Nuc->Uniprot-->Protein->EMBL->
+          SequenceI retrievedDss = retrievedSequence.getDatasetSequence() == null ? retrievedSequence
+                  : retrievedSequence.getDatasetSequence();
+          addedXref |= importCrossRefSeq(cf, dss, retrievedDss);
         }
       }
     }
@@ -470,6 +478,7 @@ public class CrossRef
       }
       if (dupeFound)
       {
+        // rebuild the search array from the filtered sourceRefs list
         dbrSourceSet = sourceRefs.toArray(new DBRefEntry[0]);
       }
     }
@@ -482,15 +491,28 @@ public class CrossRef
    * @param cf
    * @param sourceSequence
    * @param retrievedSequence
+   * @return true if retrieveSequence was imported
    */
-  private void importCrossRefSeq(AlignedCodonFrame cf,
+  private boolean importCrossRefSeq(AlignedCodonFrame cf,
           SequenceI sourceSequence, SequenceI retrievedSequence)
   {
+    /**
+     * set when retrievedSequence has been verified as a crossreference for
+     * sourceSequence
+     */
+    boolean imported = false;
     DBRefEntry[] dbr = retrievedSequence.getDBRefs();
+    List<SequenceI> newDsSeqs = new ArrayList<SequenceI>();
     if (dbr != null)
     {
       for (DBRefEntry dbref : dbr)
       {
+        SequenceI matched = findInDataset(dbref);
+        if (matched == sourceSequence)
+        {
+          // verified retrieved and source sequence cross-reference each other
+          imported = true;
+        }
         // find any entry where we should put in the sequence being
         // cross-referenced into the map
         Mapping map = dbref.getMap();
@@ -498,61 +520,70 @@ public class CrossRef
         {
           if (map.getTo() != null && map.getMap() != null)
           {
-            // TODO findInDataset requires exact sequence match but
-            // 'congruent' test is only for the mapped part
-            // maybe not a problem in practice since only ENA provide a
-            // mapping and it is to the full protein translation of CDS
-            SequenceI matched = findInDataset(dbref);
-            // matcher.findIdMatch(map.getTo());
-            if (matched != null)
+            if (map.getTo() == sourceSequence)
             {
-              /*
-               * already got an xref to this sequence; update this
-               * map to point to the same sequence, and add
-               * any new dbrefs to it
-               */
-              DBRefEntry[] toRefs = map.getTo().getDBRefs();
-              if (toRefs != null)
-              {
-                for (DBRefEntry ref : toRefs)
-                {
-                  matched.addDBRef(ref); // add or update mapping
-                }
-              }
-              map.setTo(matched);
+              // already called to import once, and most likely this sequence
+              // already imported !
+              continue;
             }
-            else
+            if (matched == null)
             {
-              if (dataset.findIndex(map.getTo()) == -1)
-              {
-                dataset.addSequence(map.getTo());
-                matcher.add(map.getTo());
-              }
+              /*
+               * sequence is new to dataset, so save a reference so it can be added. 
+               */
+              newDsSeqs.add(map.getTo());
+              continue;
             }
 
+            /*
+             * there was a matching sequence in dataset, so now, check to see if we can update the map.getTo() sequence to the existing one.
+             */
+
             try
             {
               // compare ms with dss and replace with dss in mapping
               // if map is congruent
               SequenceI ms = map.getTo();
+              // TODO findInDataset requires exact sequence match but
+              // 'congruent' test is only for the mapped part
+              // maybe not a problem in practice since only ENA provide a
+              // mapping and it is to the full protein translation of CDS
+              // matcher.findIdMatch(map.getTo());
+              // TODO addendum: if matched is shorter than getTo, this will fail
+              // - when it should really succeed.
               int sf = map.getMap().getToLowest();
               int st = map.getMap().getToHighest();
               SequenceI mappedrg = ms.getSubSequence(sf, st);
               if (mappedrg.getLength() > 0
                       && ms.getSequenceAsString().equals(
-                              sourceSequence.getSequenceAsString()))
+                              matched.getSequenceAsString()))
               {
+                /*
+                 * sequences were a match, 
+                 */
                 String msg = "Mapping updated from " + ms.getName()
                         + " to retrieved crossreference "
-                        + sourceSequence.getName();
+                        + matched.getName();
                 System.out.println(msg);
-                map.setTo(sourceSequence);
+
+                DBRefEntry[] toRefs = map.getTo().getDBRefs();
+                if (toRefs != null)
+                {
+                  /*
+                   * transfer database refs
+                   */
+                  for (DBRefEntry ref : toRefs)
+                  {
+                    matched.addDBRef(ref); // add or update mapping
+                  }
+                }
+                map.setTo(matched);
 
                 /*
                  * give the reverse reference the inverse mapping 
                  * (if it doesn't have one already)
                  */
-                setReverseMapping(sourceSequence, dbref, cf);
+                setReverseMapping(matched, dbref, cf);
 
                 /*
                  * copy sequence features as well, avoiding
@@ -578,9 +609,10 @@ public class CrossRef
                         return super.equals(o, true);
                       }
                     };
-                    sourceSequence.addSequenceFeature(newFeature);
+                    matched.addSequenceFeature(newFeature);
                   }
                 }
+
               }
               cf.addMap(retrievedSequence, map.getTo(), map.getMap());
             } catch (Exception e)
@@ -593,7 +625,26 @@ public class CrossRef
         }
       }
     }
-    retrievedSequence.updatePDBIds();
+    if (imported)
+    {
+      retrievedSequence.updatePDBIds();
+      rseqs.add(retrievedSequence);
+      if (dataset.findIndex(retrievedSequence) == -1)
+      {
+        dataset.addSequence(retrievedSequence);
+        matcher.add(retrievedSequence);
+      }
+      for (SequenceI newToSeq : newDsSeqs)
+      {
+
+        if (dataset.findIndex(newToSeq) == -1)
+        {
+          dataset.addSequence(newToSeq);
+          matcher.add(newToSeq);
+        }
+      }
+    }
+    return imported;
   }
   /**
    * Sets the inverse sequence mapping in the corresponding dbref of the mapped
@@ -637,9 +688,12 @@ public class CrossRef
   }
 
   /**
-   * Returns the first identical sequence in the dataset if any, else null
+   * Returns null or the first sequence in the dataset which is identical to
+   * xref.mapTo, and has a) a primary dbref matching xref, or if none found, the
+   * first one with an ID source|xrefacc
    * 
    * @param xref
+   *          with map and mapped-to sequence
    * @return
    */
   SequenceI findInDataset(DBRefEntry xref)
@@ -658,23 +712,37 @@ public class CrossRef
     {
       return dss;
     }
-    ;
+    DBRefEntry template = new DBRefEntry(xref.getSource(), null,
+            xref.getAccessionId());
+    /**
+     * remember the first ID match - in case we don't find a match to template
+     */
+    SequenceI firstIdMatch = null;
     for (SequenceI seq : dataset.getSequences())
     {
+      // first check primary refs.
+      List<DBRefEntry> match = DBRefUtils.searchRefs(seq.getPrimaryDBRefs()
+              .toArray(new DBRefEntry[0]), template);
+      if (match != null && match.size() == 1 && sameSequence(seq, dss))
+      {
+        return seq;
+      }
       /*
        * clumsy alternative to using SequenceIdMatcher which currently
        * returns sequences with a dbref to the matched accession id 
        * which we don't want
        */
-      if (name.equals(seq.getName()) || seq.getName().startsWith(name2))
+      if (firstIdMatch == null
+              && (name.equals(seq.getName()) || seq.getName().startsWith(
+                      name2)))
       {
         if (sameSequence(seq, dss))
         {
-          return seq;
+          firstIdMatch = seq;
         }
       }
     }
-    return null;
+    return firstIdMatch;
   }
 
   /**