JAL-2110 field 'cf' moved to stack
[jalview.git] / src / jalview / analysis / CrossRef.java
index 4e8f070..288d60e 100644 (file)
@@ -28,7 +28,6 @@ import jalview.datamodel.Mapping;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
-import jalview.util.Comparison;
 import jalview.util.DBRefUtils;
 import jalview.util.MapList;
 import jalview.ws.SequenceFetcherFactory;
@@ -54,17 +53,21 @@ public class CrossRef
   private AlignmentI dataset;
 
   /*
-   * true if we are searching for cross-references from nucleotide,
-   * i.e. for protein sequences, false if the reverse
-   */
-  private boolean fromDna;
-
-  /*
    * the sequences for which we are seeking cross-references
    */
   private SequenceI[] fromSeqs;
 
   /**
+   * matcher built from dataset
+   */
+  SequenceIdMatcher matcher;
+
+  /**
+   * sequences found by cross-ref searches to fromSeqs
+   */
+  List<SequenceI> rseqs;
+
+  /**
    * Constructor
    * 
    * @param seqs
@@ -76,7 +79,6 @@ public class CrossRef
   public CrossRef(SequenceI[] seqs, AlignmentI ds)
   {
     fromSeqs = seqs;
-    fromDna = ds.isNucleotide();
     dataset = ds.getDataset() == null ? ds : ds.getDataset();
   }
 
@@ -88,16 +90,20 @@ public class CrossRef
    * reference from another sequence in the dataset which has a cross-reference
    * to a direct DBRefEntry on the given sequence</li>
    * </ul>
+   * 
+   * @param dna
+   *          - when true, cross-references *from* dna returned. When false,
+   *          cross-references *from* protein are returned
    * @return
    */
-  public List<String> findXrefSourcesForSequences()
+  public List<String> findXrefSourcesForSequences(boolean dna)
   {
     List<String> sources = new ArrayList<String>();
     for (SequenceI seq : fromSeqs)
     {
       if (seq != null)
       {
-        findXrefSourcesForSequence(seq, sources);
+        findXrefSourcesForSequence(seq, dna, sources);
       }
     }
     return sources;
@@ -114,10 +120,14 @@ public class CrossRef
    * 
    * @param seq
    *          the sequence whose dbrefs we are searching against
+   * @param fromDna
+   *          when true, context is DNA - so sources identifying protein
+   *          products will be returned.
    * @param sources
    *          a list of sources to add matches to
    */
-  void findXrefSourcesForSequence(SequenceI seq, List<String> sources)
+  void findXrefSourcesForSequence(SequenceI seq, boolean fromDna,
+          List<String> sources)
   {
     /*
      * first find seq's xrefs (dna-to-peptide or peptide-to-dna)
@@ -130,18 +140,18 @@ public class CrossRef
        * find sequence's direct (dna-to-dna, peptide-to-peptide) xrefs
        */
       DBRefEntry[] lrfs = DBRefUtils.selectDbRefs(fromDna, seq.getDBRefs());
-      List<SequenceI> rseqs = new ArrayList<SequenceI>();
+      List<SequenceI> foundSeqs = new ArrayList<SequenceI>();
 
       /*
        * find sequences in the alignment which xref one of these DBRefs
        * i.e. is xref-ed to a common sequence identifier
        */
-      searchDatasetXrefs(seq, lrfs, rseqs, null);
+      searchDatasetXrefs(fromDna, seq, lrfs, foundSeqs, null);
 
       /*
        * add those sequences' (dna-to-peptide or peptide-to-dna) dbref sources
        */
-      for (SequenceI rs : rseqs)
+      for (SequenceI rs : foundSeqs)
       {
         DBRefEntry[] xrs = DBRefUtils
                 .selectDbRefs(!fromDna, rs.getDBRefs());
@@ -163,7 +173,10 @@ public class CrossRef
     {
       for (DBRefEntry ref : xrefs)
       {
-        String source = ref.getSource();
+        /*
+         * avoid duplication e.g. ENSEMBL and Ensembl
+         */
+        String source = DBRefUtils.getCanonicalName(ref.getSource());
         if (!sources.contains(source))
         {
           sources.add(source);
@@ -173,22 +186,30 @@ public class CrossRef
   }
 
   /**
+   * Attempts to find cross-references from the sequences provided in the
+   * constructor to the given source database. Cross-references may be found
+   * <ul>
+   * <li>in dbrefs on the sequence which hold a mapping to a sequence
+   * <ul>
+   * <li>provided with a fetched sequence (e.g. ENA translation), or</li>
+   * <li>populated previously after getting cross-references</li>
+   * </ul>
+   * <li>as other sequences in the alignment which share a dbref identifier with
+   * the sequence</li>
+   * <li>by fetching from the remote database</li>
+   * </ul>
+   * The cross-referenced sequences, and mappings to them, are added to the
+   * alignment dataset.
    * 
-   * @param seqs
-   *          sequences whose xrefs are being retrieved
-   * @param dna
-   *          true if sequences are nucleotide
    * @param source
-   * @param al
-   *          alignment to search for cross-referenced sequences (and possibly
-   *          add to)
-   * @return products (as dataset sequences)
+   * @return cross-referenced sequences (as dataset sequences)
    */
-  public Alignment findXrefSequences(String source)
+  public Alignment findXrefSequences(String source, boolean fromDna)
   {
-    List<SequenceI> rseqs = new ArrayList<SequenceI>();
+
+    rseqs = new ArrayList<SequenceI>();
     AlignedCodonFrame cf = new AlignedCodonFrame();
-    SequenceIdMatcher matcher = new SequenceIdMatcher(
+    matcher = new SequenceIdMatcher(
             dataset.getSequences());
 
     for (SequenceI seq : fromSeqs)
@@ -215,7 +236,7 @@ public class CrossRef
          *  which have a dbref to an accession id for this sequence,
          *  and add them to the results
          */
-        found = searchDatasetXrefs(dss, lrfs, rseqs, cf);
+        found = searchDatasetXrefs(fromDna, dss, lrfs, rseqs, cf);
       }
       if (xrfs == null && !found)
       {
@@ -244,13 +265,20 @@ public class CrossRef
              * for example: UNIPROT {P0CE19, P0CE20} -> EMBL {J03321, X06707}
              */
             found = true;
-            SequenceI matchInDataset = findInDataset(mappedTo);// matcher.findIdMatch(mappedTo);
+            /*
+             * problem: matcher.findIdMatch() is lenient - returns a sequence
+             * with a dbref to the search arg e.g. ENST for ENSP - wrong
+             * but findInDataset() matches ENSP when looking for Uniprot...
+             */
+            SequenceI matchInDataset = findInDataset(xref);
+            /*matcher.findIdMatch(mappedTo);*/
             if (matchInDataset != null)
             {
               if (!rseqs.contains(matchInDataset))
               {
                 rseqs.add(matchInDataset);
               }
+              refIterator.remove();
               continue;
             }
             SequenceI rsq = new Sequence(mappedTo);
@@ -262,12 +290,14 @@ public class CrossRef
               if (fromDna)
               {
                 // map is from dna seq to a protein product
-                cf.addMap(dss, rsq, xref.getMap().getMap());
+                cf.addMap(dss, rsq, xref.getMap().getMap(), xref.getMap()
+                        .getMappedFromId());
               }
               else
               {
                 // map should be from protein seq to its coding dna
-                cf.addMap(rsq, dss, xref.getMap().getMap().getInverse());
+                cf.addMap(rsq, dss, xref.getMap().getMap().getInverse(),
+                        xref.getMap().getMappedFromId());
               }
             }
           }
@@ -279,7 +309,7 @@ public class CrossRef
                   + xref.getAccessionId());
           if (matchedSeq != null)
           {
-            if (constructMapping(seq, matchedSeq, xref, cf))
+            if (constructMapping(seq, matchedSeq, xref, cf, fromDna))
             {
               found = true;
             }
@@ -290,7 +320,7 @@ public class CrossRef
         {
           // do a bit more work - search for sequences with references matching
           // xrefs on this sequence.
-          found = searchDataset(dss, xref, rseqs, cf, false);
+          found = searchDataset(fromDna, dss, xref, rseqs, cf, false);
         }
         if (found)
         {
@@ -303,167 +333,232 @@ public class CrossRef
        */
       if (!sourceRefs.isEmpty())
       {
-        ASequenceFetcher sftch = SequenceFetcherFactory
-                .getSequenceFetcher();
-        SequenceI[] retrieved = null;
-        try
-        {
-          retrieved = sftch.getSequences(sourceRefs, !fromDna);
-        } catch (Exception e)
-        {
-          System.err
-                  .println("Problem whilst retrieving cross references for Sequence : "
-                          + seq.getName());
-          e.printStackTrace();
-        }
+        retrieveCrossRef(sourceRefs, seq, xrfs, fromDna, cf);
+      }
+    }
 
-        if (retrieved != null)
+    Alignment ral = null;
+    if (rseqs.size() > 0)
+    {
+      ral = new Alignment(rseqs.toArray(new SequenceI[rseqs.size()]));
+      if (!cf.isEmpty())
+      {
+        dataset.addCodonFrame(cf);
+      }
+    }
+    return ral;
+  }
+
+  private void retrieveCrossRef(List<DBRefEntry> sourceRefs, SequenceI seq,
+          DBRefEntry[] xrfs, boolean fromDna, AlignedCodonFrame cf)
+  {
+    ASequenceFetcher sftch = SequenceFetcherFactory.getSequenceFetcher();
+    SequenceI[] retrieved = null;
+    SequenceI dss = seq.getDatasetSequence() == null ? seq : seq
+            .getDatasetSequence();
+    try
+    {
+      retrieved = sftch.getSequences(sourceRefs, !fromDna);
+    } catch (Exception e)
+    {
+      System.err
+              .println("Problem whilst retrieving cross references for Sequence : "
+                      + seq.getName());
+      e.printStackTrace();
+    }
+
+    if (retrieved != null)
+    {
+      updateDbrefMappings(seq, xrfs, retrieved, cf, fromDna);
+      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();
+        DBRefEntry[] dbr = retrievedSequence.getDBRefs();
+        if (dbr != null)
         {
-          updateDbrefMappings(seq, xrfs, retrieved, cf);
-          for (SequenceI retrievedSequence : retrieved)
+          for (DBRefEntry dbref : dbr)
           {
-            SequenceI retrievedDss = retrievedSequence.getDatasetSequence() == null ? retrievedSequence
-                    : retrievedSequence.getDatasetSequence();
-            DBRefEntry[] dbr = retrievedSequence.getDBRefs();
-            if (dbr != null)
+            // find any entry where we should put in the sequence being
+            // cross-referenced into the map
+            Mapping map = dbref.getMap();
+            if (map != null)
             {
-              for (DBRefEntry dbref : dbr)
+              if (map.getTo() != null && map.getMap() != null)
               {
-                // find any entry where we should put in the sequence being
-                // cross-referenced into the map
-                Mapping map = dbref.getMap();
-                if (map != 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() != null && map.getMap() != null)
+                  /*
+                   * 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)
                   {
-                    // TODO findInDataset requires exact sequence match but
-                    // 'congruent' test only for the mapped part
-                    SequenceI matched = findInDataset(map.getTo());// matcher.findIdMatch(map.getTo());
-                    if (matched != null)
+                    for (DBRefEntry ref : toRefs)
                     {
-                      /*
-                       * 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);
+                      matched.addDBRef(ref); // add or update mapping
                     }
-                    else
-                    {
-                      matcher.add(map.getTo());
-                    }
-                    try
+                  }
+                  map.setTo(matched);
+                }
+                else
+                {
+                  matcher.add(map.getTo());
+                }
+                try
+                {
+                  // compare ms with dss and replace with dss in mapping
+                  // if map is congruent
+                  SequenceI ms = map.getTo();
+                  int sf = map.getMap().getToLowest();
+                  int st = map.getMap().getToHighest();
+                  SequenceI mappedrg = ms.getSubSequence(sf, st);
+                  // SequenceI loc = dss.getSubSequence(sf, st);
+                  if (mappedrg.getLength() > 0
+                          && ms.getSequenceAsString().equals(
+                                  dss.getSequenceAsString()))
+                  // && mappedrg.getSequenceAsString().equals(
+                  // loc.getSequenceAsString()))
+                  {
+                    String msg = "Mapping updated from " + ms.getName()
+                            + " to retrieved crossreference "
+                            + dss.getName();
+                    System.out.println(msg);
+                    map.setTo(dss);
+
+                    /*
+                     * give the reverse reference the inverse mapping 
+                     * (if it doesn't have one already)
+                     */
+                    setReverseMapping(dss, dbref, cf);
+
+                    /*
+                     * copy sequence features as well, avoiding
+                     * duplication (e.g. same variation from two 
+                     * transcripts)
+                     */
+                    SequenceFeature[] sfs = ms.getSequenceFeatures();
+                    if (sfs != null)
                     {
-                      // compare ms with dss and replace with dss in mapping
-                      // if map is congruent
-                      SequenceI ms = map.getTo();
-                      int sf = map.getMap().getToLowest();
-                      int st = map.getMap().getToHighest();
-                      SequenceI mappedrg = ms.getSubSequence(sf, st);
-                      // SequenceI loc = dss.getSubSequence(sf, st);
-                      if (mappedrg.getLength() > 0
-                              && ms.getSequenceAsString().equals(
-                                      dss.getSequenceAsString()))
-                      // && mappedrg.getSequenceAsString().equals(
-                      // loc.getSequenceAsString()))
+                      for (SequenceFeature feat : sfs)
                       {
-                        String msg = "Mapping updated from " + ms.getName()
-                                + " to retrieved crossreference "
-                                + dss.getName();
-                        System.out.println(msg);
-                        // method to update all refs of existing To on
-                        // retrieved sequence with dss and merge any props
-                        // on To onto dss.
-                        // TODO don't we have to change the mapped to ranges
-                        // if not to the whole sequence?
-                        map.setTo(dss);
                         /*
-                         * copy sequence features as well, avoiding
-                         * duplication (e.g. same variation from 2 
-                         * transcripts)
+                         * make a flyweight feature object which ignores Parent
+                         * attribute in equality test; this avoids creating many
+                         * otherwise duplicate exon features on genomic sequence
                          */
-                        SequenceFeature[] sfs = ms.getSequenceFeatures();
-                        if (sfs != null)
+                        SequenceFeature newFeature = new SequenceFeature(
+                                feat)
                         {
-                          for (SequenceFeature feat : sfs)
+                          @Override
+                          public boolean equals(Object o)
                           {
-                            /*
-                             * make a flyweight feature object which ignores Parent
-                             * attribute in equality test, to avoid creating many
-                             * otherwise duplicate exon features on genomic sequence
-                             */
-                            SequenceFeature newFeature = new SequenceFeature(
-                                    feat)
-                            {
-                              @Override
-                              public boolean equals(Object o)
-                              {
-                                return super.equals(o, true);
-                              }
-                            };
-                            dss.addSequenceFeature(newFeature);
+                            return super.equals(o, true);
                           }
-                        }
+                        };
+                        dss.addSequenceFeature(newFeature);
                       }
-                      cf.addMap(retrievedDss, map.getTo(), map.getMap());
-                    } catch (Exception e)
-                    {
-                      System.err
-                              .println("Exception when consolidating Mapped sequence set...");
-                      e.printStackTrace(System.err);
                     }
                   }
+                  cf.addMap(retrievedDss, map.getTo(), map.getMap());
+                } catch (Exception e)
+                {
+                  System.err
+                          .println("Exception when consolidating Mapped sequence set...");
+                  e.printStackTrace(System.err);
                 }
               }
             }
-            retrievedSequence.updatePDBIds();
-            rseqs.add(retrievedSequence);
-            dataset.addSequence(retrievedDss);
-            matcher.add(retrievedSequence);
           }
         }
+        retrievedSequence.updatePDBIds();
+        rseqs.add(retrievedDss);
+        dataset.addSequence(retrievedDss);
+        matcher.add(retrievedDss);
       }
     }
-
-    Alignment ral = null;
-    if (rseqs.size() > 0)
+  }
+  /**
+   * Sets the inverse sequence mapping in the corresponding dbref of the mapped
+   * to sequence (if any). This is used after fetching a cross-referenced
+   * sequence, if the fetched sequence has a mapping to the original sequence,
+   * to set the mapping in the original sequence's dbref.
+   * 
+   * @param mapFrom
+   *          the sequence mapped from
+   * @param dbref
+   * @param mappings
+   */
+  void setReverseMapping(SequenceI mapFrom, DBRefEntry dbref,
+          AlignedCodonFrame mappings)
+  {
+    SequenceI mapTo = dbref.getMap().getTo();
+    if (mapTo == null)
     {
-      ral = new Alignment(rseqs.toArray(new SequenceI[rseqs.size()]));
-      if (cf != null && !cf.isEmpty())
+      return;
+    }
+    DBRefEntry[] dbrefs = mapTo.getDBRefs();
+    if (dbrefs == null)
+    {
+      return;
+    }
+    for (DBRefEntry toRef : dbrefs)
+    {
+      if (toRef.hasMap() && mapFrom == toRef.getMap().getTo())
       {
-        ral.addCodonFrame(cf);
+        /*
+         * found the reverse dbref; update its mapping if null
+         */
+        if (toRef.getMap().getMap() == null)
+        {
+          MapList inverse = dbref.getMap().getMap().getInverse();
+          toRef.getMap().setMap(inverse);
+          mappings.addMap(mapTo, mapFrom, inverse);
+        }
       }
     }
-    return ral;
   }
 
   /**
    * Returns the first identical sequence in the dataset if any, else null
    * 
-   * @param mappedTo
+   * @param xref
    * @return
    */
-  SequenceI findInDataset(SequenceI mappedTo)
+  SequenceI findInDataset(DBRefEntry xref)
   {
-    if (mappedTo == null)
+    if (xref == null || !xref.hasMap() || xref.getMap().getTo() == null)
     {
       return null;
     }
-    SequenceI dss = mappedTo.getDatasetSequence() == null ? mappedTo
-            : mappedTo.getDatasetSequence();
+    SequenceI mapsTo = xref.getMap().getTo();
+    String name = xref.getAccessionId();
+    String name2 = xref.getSource() + "|" + name;
+    SequenceI dss = mapsTo.getDatasetSequence() == null ? mapsTo : mapsTo
+            .getDatasetSequence();
     for (SequenceI seq : dataset.getSequences())
     {
-      if (sameSequence(seq, dss))
+      /*
+       * 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))
       {
-        return seq;
+        if (sameSequence(seq, dss))
+        {
+          return seq;
+        }
       }
     }
     return null;
@@ -520,33 +615,42 @@ public class CrossRef
    * @param retrieved
    * @param acf
    */
-  void updateDbrefMappings(SequenceI mapFrom,
-          DBRefEntry[] xrefs, SequenceI[] retrieved, AlignedCodonFrame acf)
+  void updateDbrefMappings(SequenceI mapFrom, DBRefEntry[] xrefs,
+          SequenceI[] retrieved, AlignedCodonFrame acf, boolean fromDna)
   {
-    SequenceIdMatcher matcher = new SequenceIdMatcher(retrieved);
+    SequenceIdMatcher idMatcher = new SequenceIdMatcher(retrieved);
     for (DBRefEntry xref : xrefs)
     {
       if (!xref.hasMap())
       {
         String targetSeqName = xref.getSource() + "|"
                 + xref.getAccessionId();
-        SequenceI[] matches = matcher.findAllIdMatches(targetSeqName);
+        SequenceI[] matches = idMatcher.findAllIdMatches(targetSeqName);
         if (matches == null)
         {
           return;
         }
         for (SequenceI seq : matches)
         {
-          constructMapping(mapFrom, seq, xref, acf);
+          constructMapping(mapFrom, seq, xref, acf, fromDna);
         }
       }
     }
   }
 
   /**
-   * Tries to make a mapping from dna to protein. If successful, adds the
-   * mapping to the dbref and the mappings collection and answers true,
-   * otherwise answers false.
+   * Tries to make a mapping between sequences. If successful, adds the mapping
+   * to the dbref and the mappings collection and answers true, otherwise
+   * answers false. The following methods of making are mapping are tried in
+   * turn:
+   * <ul>
+   * <li>if 'mapTo' holds a mapping to 'mapFrom', take the inverse; this is, for
+   * example, the case after fetching EMBL cross-references for a Uniprot
+   * sequence</li>
+   * <li>else check if the dna translates exactly to the protein (give or take
+   * start and stop codons></li>
+   * <li>else try to map based on CDS features on the dna sequence</li>
+   * </ul>
    * 
    * @param mapFrom
    * @param mapTo
@@ -555,9 +659,32 @@ public class CrossRef
    * @return
    */
   boolean constructMapping(SequenceI mapFrom, SequenceI mapTo,
-          DBRefEntry xref, AlignedCodonFrame mappings)
+          DBRefEntry xref, AlignedCodonFrame mappings, boolean fromDna)
   {
     MapList mapping = null;
+
+    /*
+     * look for a reverse mapping, if found make its inverse
+     */
+    if (mapTo.getDBRefs() != null)
+    {
+      for (DBRefEntry dbref : mapTo.getDBRefs())
+      {
+        String name = dbref.getSource() + "|" + dbref.getAccessionId();
+        if (dbref.hasMap() && mapFrom.getName().startsWith(name))
+        {
+          /*
+           * looks like we've found a map from 'mapTo' to 'mapFrom'
+           * - invert it to make the mapping the other way 
+           */
+          MapList reverse = dbref.getMap().getMap().getInverse();
+          xref.setMap(new Mapping(mapTo, reverse));
+          mappings.addMap(mapFrom, mapTo, reverse);
+          return true;
+        }
+      }
+    }
+
     if (fromDna)
     {
       mapping = AlignmentUtils.mapCdnaToProtein(mapTo, mapFrom);
@@ -575,6 +702,20 @@ public class CrossRef
       return false;
     }
     xref.setMap(new Mapping(mapTo, mapping));
+
+    /*
+     * and add a reverse DbRef with the inverse mapping
+     */
+    if (mapFrom.getDatasetSequence() != null
+            && mapFrom.getDatasetSequence().getSourceDBRef() != null)
+    {
+      DBRefEntry dbref = new DBRefEntry(mapFrom.getDatasetSequence()
+              .getSourceDBRef());
+      dbref.setMap(new Mapping(mapFrom.getDatasetSequence(), mapping
+              .getInverse()));
+      mapTo.addDBRef(dbref);
+    }
+
     if (fromDna)
     {
       AlignmentUtils.computeProteinFeatures(mapFrom, mapTo, mapping);
@@ -593,13 +734,16 @@ public class CrossRef
    * dataset (that is not equal to sequenceI) Identifies matching DBRefEntry
    * based on source and accession string only - Map and Version are nulled.
    * 
+   * @param fromDna
+   *          - true if context was searching from Dna sequences, false if
+   *          context was searching from Protein sequences
    * @param sequenceI
    * @param lrfs
-   * @param rseqs
+   * @param foundSeqs
    * @return true if matches were found.
    */
-  private boolean searchDatasetXrefs(SequenceI sequenceI,
-          DBRefEntry[] lrfs, List<SequenceI> rseqs, AlignedCodonFrame cf)
+  private boolean searchDatasetXrefs(boolean fromDna, SequenceI sequenceI,
+          DBRefEntry[] lrfs, List<SequenceI> foundSeqs, AlignedCodonFrame cf)
   {
     boolean found = false;
     if (lrfs == null)
@@ -612,7 +756,7 @@ public class CrossRef
       // add in wildcards
       xref.setVersion(null);
       xref.setMap(null);
-      found |= searchDataset(sequenceI, xref, rseqs, cf, false);
+      found |= searchDataset(fromDna, sequenceI, xref, foundSeqs, cf, false);
     }
     return found;
   }
@@ -621,20 +765,33 @@ public class CrossRef
    * Searches dataset for DBRefEntrys matching the given one (xrf) and adds the
    * associated sequence to rseqs
    * 
-   * @param sequenceI
+   * @param fromDna
+   *          true if context was searching for refs *from* dna sequence, false
+   *          if context was searching for refs *from* protein sequence
+   * @param fromSeq
    *          a sequence to ignore (start point of search)
    * @param xrf
    *          a cross-reference to try to match
-   * @param rseqs
+   * @param foundSeqs
    *          result list to add to
-   * @param cf
+   * @param mappings
    *          a set of sequence mappings to add to
    * @param direct
-   *          - search all references or only subset
+   *          - indicates the type of relationship between returned sequences,
+   *          xrf, and sequenceI that is required.
+   *          <ul>
+   *          <li>direct implies xrf is a primary reference for sequenceI AND
+   *          the sequences to be located (eg a uniprot ID for a protein
+   *          sequence, and a uniprot ref on a transcript sequence).</li>
+   *          <li>indirect means xrf is a cross reference with respect to
+   *          sequenceI or all the returned sequences (eg a genomic reference
+   *          associated with a locus and one or more transcripts)</li>
+   *          </ul>
    * @return true if relationship found and sequence added.
    */
-  boolean searchDataset(SequenceI sequenceI, DBRefEntry xrf,
-          List<SequenceI> rseqs, AlignedCodonFrame cf, boolean direct)
+  boolean searchDataset(boolean fromDna, SequenceI fromSeq,
+          DBRefEntry xrf, List<SequenceI> foundSeqs, AlignedCodonFrame mappings,
+          boolean direct)
   {
     boolean found = false;
     if (dataset == null)
@@ -656,9 +813,13 @@ public class CrossRef
           if (nxt.getDatasetSequence() != null)
           {
             System.err
-                    .println("Implementation warning: getProducts passed a dataset alignment without dataset sequences in it!");
+                    .println("Implementation warning: CrossRef initialised with a dataset alignment with non-dataset sequences in it! ("
+                            + nxt.getDisplayId(true)
+                            + " has ds reference "
+                            + nxt.getDatasetSequence().getDisplayId(true)
+                            + ")");
           }
-          if (nxt == sequenceI || nxt == sequenceI.getDatasetSequence())
+          if (nxt == fromSeq || nxt == fromSeq.getDatasetSequence())
           {
             continue;
           }
@@ -667,8 +828,7 @@ public class CrossRef
            * complementary type if !direct
            */
           {
-            boolean isDna = Comparison
-                    .isNucleotide(new SequenceI[] { nxt });
+            boolean isDna = !nxt.isProtein();
             if (direct ? (isDna != fromDna) : (isDna == fromDna))
             {
               // skip this sequence because it is wrong molecule type
@@ -679,30 +839,29 @@ public class CrossRef
           // look for direct or indirect references in common
           DBRefEntry[] poss = nxt.getDBRefs();
           List<DBRefEntry> cands = null;
-          /*
-           * TODO does this make any sense?
-           * if 'direct', search the dbrefs for xrf
-           * else, filter the dbrefs by type and then search for xrf
-           * - the result is the same isn't it?
-           */
-          if (direct)
-          {
-            cands = DBRefUtils.searchRefs(poss, xrf);
-          }
-          else
-          {
-            poss = DBRefUtils.selectDbRefs(!fromDna, poss);
-            cands = DBRefUtils.searchRefs(poss, xrf);
-          }
+
+          // todo: indirect specifies we select either direct references to nxt
+          // that match xrf which is indirect to sequenceI, or indirect
+          // references to nxt that match xrf which is direct to sequenceI
+          cands = DBRefUtils.searchRefs(poss, xrf);
+          // else
+          // {
+          // poss = DBRefUtils.selectDbRefs(nxt.isProtein()!fromDna, poss);
+          // cands = DBRefUtils.searchRefs(poss, xrf);
+          // }
           if (!cands.isEmpty())
           {
-            if (!rseqs.contains(nxt))
+            if (!foundSeqs.contains(nxt))
             {
               found = true;
-              rseqs.add(nxt);
-              if (cf != null)
+              foundSeqs.add(nxt);
+              if (mappings != null && !direct)
               {
-                // don't search if we aren't given a codon map object
+                /*
+                 * if the matched sequence has mapped dbrefs to
+                 * protein product / cdna, add equivalent mappings to
+                 * our source sequence
+                 */
                 for (DBRefEntry candidate : cands)
                 {
                   Mapping mapping = candidate.getMap();
@@ -712,23 +871,21 @@ public class CrossRef
                     if (mapping.getTo() != null
                             && map.getFromRatio() != map.getToRatio())
                     {
-                      // get sense of map correct for adding to product
-                      // alignment.
-                      if (fromDna)
+                      /*
+                       * add a mapping, as from dna to peptide sequence
+                       */
+                      if (map.getFromRatio() == 3)
                       {
-                        // map is from dna seq to a protein product
-                        cf.addMap(sequenceI, nxt, map);
+                        mappings.addMap(nxt, fromSeq, map);
                       }
                       else
                       {
-                        // map should be from protein seq to its coding dna
-                        cf.addMap(nxt, sequenceI, map.getInverse());
+                        mappings.addMap(nxt, fromSeq, map.getInverse());
                       }
                     }
                   }
                 }
               }
-              // TODO: add mapping between sequences if necessary
             }
           }
         }