JAL-2110 unit tests for and minor refactoring of CrossRef
[jalview.git] / src / jalview / analysis / CrossRef.java
index 7238239..8fd0706 100644 (file)
  */
 package jalview.analysis;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Vector;
-
 import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefEntry;
-import jalview.datamodel.DBRefSource;
+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.ws.SequenceFetcher;
+import jalview.util.MapList;
+import jalview.ws.SequenceFetcherFactory;
 import jalview.ws.seqfetcher.ASequenceFetcher;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Functions for cross-referencing sequence databases. user must first specify
  * if cross-referencing from protein or dna (set dna==true)
@@ -44,219 +46,204 @@ import jalview.ws.seqfetcher.ASequenceFetcher;
  */
 public class CrossRef
 {
-  /**
-   * Select just the DNA or protein references for a protein or dna sequence
-   * 
-   * @param fromDna
-   *          if true, select references from DNA (i.e. Protein databases), else
-   *          DNA database references
-   * @param refs
-   *          a set of references to select from
-   * @return
+  /*
+   * A sub-class that ignores Parent attribute when comparing sequence 
+   * features. This avoids 'duplicate' CDS features that only
+   * differ in their parent Transcript ids.
    */
-  public static DBRefEntry[] findXDbRefs(boolean fromDna, DBRefEntry[] refs)
+  class MySequenceFeature extends SequenceFeature
   {
-    return DBRefUtils.selectRefs(refs, fromDna ? DBRefSource.PROTEINDBS
-            : DBRefSource.DNACODINGDBS);
-    // could attempt to find other cross
-    // refs here - ie PDB xrefs
-    // (not dna, not protein seq)
-  }
+    private SequenceFeature feat;
 
-  /**
-   * @param dna
-   *          true if seqs are DNA seqs
-   * @param seqs
-   * @return a list of sequence database cross reference source types
-   */
-  public static String[] findSequenceXrefTypes(boolean dna, SequenceI[] seqs)
-  {
-    return findSequenceXrefTypes(dna, seqs, null);
+    MySequenceFeature(SequenceFeature sf)
+    {
+      this.feat = sf;
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+      return feat.equals(o, true);
+    }
   }
 
   /**
-   * Indirect references are references from other sequences from the dataset to
-   * any of the direct DBRefEntrys on the given sequences.
+
+   * Returns a list of distinct database sources for which sequences have either
+   * <ul>
+   * <li>a (dna-to-protein or protein-to-dna) cross-reference</li>
+   * <li>an indirect cross-reference - a (dna-to-protein or protein-to-dna)
+   * reference from another sequence in the dataset which has a cross-reference
+   * to a direct DBRefEntry on the given sequence</li>
+   * </ul>
    * 
    * @param dna
-   *          true if seqs are DNA seqs
+   *          true if seqs are nucleotide
    * @param seqs
-   * @return a list of sequence database cross reference source types
+   *          sequences whose xrefs we are seeking
+   * @param dataset
+   *          an alignment to search for indirect references
+   * @return
    */
-  public static String[] findSequenceXrefTypes(boolean dna,
+  public static List<String> findXrefSourcesForSequences(boolean dna,
           SequenceI[] seqs, AlignmentI dataset)
   {
-    String[] dbrefs = null;
-    List<String> refs = new ArrayList<String>();
-    for (int s = 0; s < seqs.length; s++)
+    List<String> sources = new ArrayList<String>();
+    for (SequenceI seq : seqs)
     {
-      if (seqs[s] != null)
+      if (seq != null)
       {
-        SequenceI dss = seqs[s];
-        while (dss.getDatasetSequence() != null)
-        {
-          dss = dss.getDatasetSequence();
-        }
-        DBRefEntry[] rfs = findXDbRefs(dna, dss.getDBRef());
-        for (int r = 0; rfs != null && r < rfs.length; r++)
-        {
-          if (!refs.contains(rfs[r].getSource()))
-          {
-            refs.add(rfs[r].getSource());
-          }
-        }
-        if (dataset != null)
-        {
-          // search for references to this sequence's direct references.
-          DBRefEntry[] lrfs = CrossRef
-                  .findXDbRefs(!dna, seqs[s].getDBRef());
-          List<SequenceI> rseqs = new ArrayList<SequenceI>();
-          CrossRef.searchDatasetXrefs(seqs[s], !dna, lrfs, dataset, rseqs,
-                  null); // don't need to specify codon frame for mapping here
-          for (SequenceI rs : rseqs)
-          {
-            DBRefEntry[] xrs = findXDbRefs(dna, rs.getDBRef()); // not used??
-            for (int r = 0; rfs != null && r < rfs.length; r++)
-            {
-              if (!refs.contains(rfs[r].getSource()))
-              {
-                refs.add(rfs[r].getSource());
-              }
-            }
-          }
-        }
+        findXrefSourcesForSequence(seq, dna, dataset, sources);
       }
     }
-    if (refs.size() > 0)
-    {
-      dbrefs = new String[refs.size()];
-      refs.toArray(dbrefs);
-    }
-    return dbrefs;
+    return sources;
   }
 
-  /*
-   * if (dna) { if (rfs[r].hasMap()) { // most likely this is a protein cross
-   * reference if (!refs.contains(rfs[r].getSource())) {
-   * refs.addElement(rfs[r].getSource()); } } }
+  /**
+   * Returns a list of distinct database sources for which a sequence has either
+   * <ul>
+   * <li>a (dna-to-protein or protein-to-dna) cross-reference</li>
+   * <li>an indirect cross-reference - a (dna-to-protein or protein-to-dna)
+   * reference from another sequence in the dataset which has a cross-reference
+   * to a direct DBRefEntry on the given sequence</li>
+   * </ul>
+   * 
+   * @param seq
+   *          the sequence whose dbrefs we are searching against
+   * @param dna
+   *          true if the sequence is nucleotide
+   * @param dataset
+   *          an alignment to search for indirect references
+   * @param sources
+   *          a list of sources to add matches to
    */
-  public static boolean hasCdnaMap(SequenceI[] seqs)
+  static void findXrefSourcesForSequence(SequenceI seq, boolean dna,
+          AlignmentI dataset, List<String> sources)
   {
-    String[] reftypes = findSequenceXrefTypes(false, seqs);
-    for (int s = 0; s < reftypes.length; s++)
+    /*
+     * first find seq's xrefs (dna-to-peptide or peptide-to-dna)
+     */
+    DBRefEntry[] rfs = DBRefUtils.selectDbRefs(!dna, seq.getDBRefs());
+    addXrefsToSources(rfs, sources);
+    if (dataset != null)
     {
-      if (reftypes.equals(DBRefSource.EMBLCDS))
+      /*
+       * find sequence's direct (dna-to-dna, peptide-to-peptide) xrefs
+       */
+      DBRefEntry[] lrfs = DBRefUtils.selectDbRefs(dna, seq.getDBRefs());
+      List<SequenceI> rseqs = new ArrayList<SequenceI>();
+
+      /*
+       * find sequences in the alignment which xref one of these DBRefs
+       * i.e. is xref-ed to a common sequence identifier
+       */
+      CrossRef.searchDatasetXrefs(seq, !dna, lrfs, dataset, rseqs, null);
+
+      /*
+       * add those sequences' (dna-to-peptide or peptide-to-dna) dbref sources
+       */
+      for (SequenceI rs : rseqs)
       {
-        return true;
-        // no map
+        DBRefEntry[] xrs = DBRefUtils.selectDbRefs(!dna, rs.getDBRefs());
+        addXrefsToSources(xrs, sources);
       }
     }
-    return false;
   }
 
-  public static SequenceI[] getCdnaMap(SequenceI[] seqs)
+  /**
+   * Helper method that adds the source identifiers of some cross-references to
+   * a (non-redundant) list of database sources
+   * 
+   * @param xrefs
+   * @param sources
+   */
+  static void addXrefsToSources(DBRefEntry[] xrefs, List<String> sources)
   {
-    Vector cseqs = new Vector();
-    for (int s = 0; s < seqs.length; s++)
+    if (xrefs != null)
     {
-      DBRefEntry[] cdna = findXDbRefs(true, seqs[s].getDBRef());
-      for (int c = 0; c < cdna.length; c++)
+      for (DBRefEntry ref : xrefs)
       {
-        if (cdna[c].getSource().equals(DBRefSource.EMBLCDS))
+        String source = ref.getSource();
+        if (!sources.contains(source))
         {
-          System.err
-                  .println("TODO: unimplemented sequence retrieval for coding region sequence.");
-          // TODO: retrieve CDS dataset sequences
-          // need global dataset sequence retriever/resolver to reuse refs
-          // and construct Mapping entry.
-          // insert gaps in CDS according to peptide gaps.
-          // add gapped sequence to cseqs
+          sources.add(source);
         }
       }
     }
-    if (cseqs.size() > 0)
-    {
-      SequenceI[] rsqs = new SequenceI[cseqs.size()];
-      cseqs.copyInto(rsqs);
-      return rsqs;
-    }
-    return null;
-
-  }
-
-  /**
-   * 
-   * @param dna
-   * @param seqs
-   * @return
-   */
-  public static Alignment findXrefSequences(SequenceI[] seqs, boolean dna,
-          String source)
-  {
-    return findXrefSequences(seqs, dna, source, null);
   }
 
   /**
    * 
    * @param seqs
+   *          sequences whose xrefs are being retrieved
    * @param dna
+   *          true if sequences are nucleotide
    * @param source
-   * @param dataset
-   *          alignment to search for product sequences.
+   * @param al
+   *          alignment to search for cross-referenced sequences (and possibly
+   *          add to)
    * @return products (as dataset sequences)
    */
-  public static Alignment findXrefSequences(SequenceI[] seqs, boolean dna,
-          String source, AlignmentI dataset)
+  public static Alignment findXrefSequences(SequenceI[] seqs,
+          final boolean dna, final String source, AlignmentI al)
   {
+    AlignmentI dataset = al.getDataset() == null ? al : al.getDataset();
     List<SequenceI> rseqs = new ArrayList<SequenceI>();
-    Alignment ral = null;
-    AlignedCodonFrame cf = new AlignedCodonFrame(); // nominal width
-    for (int s = 0; s < seqs.length; s++)
+    AlignedCodonFrame cf = new AlignedCodonFrame();
+    for (SequenceI seq : seqs)
     {
-      SequenceI dss = seqs[s];
+      SequenceI dss = seq;
       while (dss.getDatasetSequence() != null)
       {
         dss = dss.getDatasetSequence();
       }
       boolean found = false;
-      DBRefEntry[] xrfs = CrossRef.findXDbRefs(dna, dss.getDBRef());
+      DBRefEntry[] xrfs = DBRefUtils.selectDbRefs(!dna, dss.getDBRefs());
       if ((xrfs == null || xrfs.length == 0) && dataset != null)
       {
-        System.out.println("Attempting to find ds Xrefs refs.");
-        DBRefEntry[] lrfs = CrossRef.findXDbRefs(!dna, seqs[s].getDBRef());
-        // less ambiguous would be a 'find primary dbRefEntry' method.
-        // filter for desired source xref here
+        /*
+         * found no suitable dbrefs on sequence - look for sequences in the
+         * alignment which share a dbref with this one
+         */
+        DBRefEntry[] lrfs = DBRefUtils.selectDbRefs(dna, seq.getDBRefs());
+
+        /*
+         * find sequences (except this one!), of complementary type,
+         *  which have a dbref to an accession id for this sequence,
+         *  and add them to the results
+         */
         found = CrossRef.searchDatasetXrefs(dss, !dna, lrfs, dataset,
                 rseqs, cf);
       }
       for (int r = 0; xrfs != null && r < xrfs.length; r++)
       {
-        if (source != null && !source.equals(xrfs[r].getSource()))
+        DBRefEntry xref = xrfs[r];
+        if (source != null && !source.equals(xref.getSource()))
         {
           continue;
         }
-        if (xrfs[r].hasMap())
+        if (xref.hasMap())
         {
-          if (xrfs[r].getMap().getTo() != null)
+          if (xref.getMap().getTo() != null)
           {
-            SequenceI rsq = new Sequence(xrfs[r].getMap().getTo());
+            found = true;
+            SequenceI rsq = new Sequence(xref.getMap().getTo());
             rseqs.add(rsq);
-            if (xrfs[r].getMap().getMap().getFromRatio() != xrfs[r]
+            if (xref.getMap().getMap().getFromRatio() != xref
                     .getMap().getMap().getToRatio())
             {
               // get sense of map correct for adding to product alignment.
               if (dna)
               {
                 // map is from dna seq to a protein product
-                cf.addMap(dss, rsq, xrfs[r].getMap().getMap());
+                cf.addMap(dss, rsq, xref.getMap().getMap());
               }
               else
               {
                 // map should be from protein seq to its coding dna
-                cf.addMap(rsq, dss, xrfs[r].getMap().getMap().getInverse());
+                cf.addMap(rsq, dss, xref.getMap().getMap().getInverse());
               }
             }
-            found = true;
           }
         }
         if (!found)
@@ -265,9 +252,10 @@ public class CrossRef
           // xrefs on this sequence.
           if (dataset != null)
           {
-            found |= searchDataset(dss, xrfs[r], dataset, rseqs, cf); // ,false,!dna);
+            found = searchDataset(dss, xref, dataset, rseqs, cf, false,/*true?*/
+                    !dna);
             if (found)
-             {
+            {
               xrfs[r] = null; // we've recovered seqs for this one.
             }
           }
@@ -277,12 +265,8 @@ public class CrossRef
       {
         if (xrfs != null && xrfs.length > 0)
         {
-          // Try and get the sequence reference...
-          /*
-           * Ideal world - we ask for a sequence fetcher implementation here if
-           * (jalview.io.RunTimeEnvironment.getSequenceFetcher()) (
-           */
-          ASequenceFetcher sftch = new SequenceFetcher();
+          ASequenceFetcher sftch = SequenceFetcherFactory
+                  .getSequenceFetcher();
           SequenceI[] retrieved = null;
           int l = xrfs.length;
           for (int r = 0; r < xrfs.length; r++)
@@ -299,8 +283,8 @@ public class CrossRef
           }
           if (l > 0)
           {
-            System.out
-                    .println("Attempting to retrieve cross referenced sequences.");
+            // System.out
+            // .println("Attempting to retrieve cross referenced sequences.");
             DBRefEntry[] t = new DBRefEntry[l];
             l = 0;
             for (int r = 0; r < xrfs.length; r++)
@@ -313,37 +297,59 @@ public class CrossRef
             xrfs = t;
             try
             {
-              retrieved = sftch.getSequences(xrfs); // problem here is we don't
-              // know which of xrfs
-              // resulted in which
+              retrieved = sftch.getSequences(xrfs, !dna);
+              // problem here is we don't know which of xrfs resulted in which
               // retrieved element
             } catch (Exception e)
             {
               System.err
                       .println("Problem whilst retrieving cross references for Sequence : "
-                              + seqs[s].getName());
+                              + seq.getName());
               e.printStackTrace();
             }
+
             if (retrieved != null)
             {
+              updateDbrefMappings(dna, seq, xrfs, retrieved, cf);
+
+              SequenceIdMatcher matcher = new SequenceIdMatcher(
+                      dataset.getSequences());
+              List<SequenceFeature> copiedFeatures = new ArrayList<SequenceFeature>();
+              CrossRef me = new CrossRef();
               for (int rs = 0; rs < retrieved.length; rs++)
               {
                 // TODO: examine each sequence for 'redundancy'
-                jalview.datamodel.DBRefEntry[] dbr = retrieved[rs]
-                        .getDBRef();
+                DBRefEntry[] dbr = retrieved[rs].getDBRefs();
                 if (dbr != null && dbr.length > 0)
                 {
                   for (int di = 0; di < dbr.length; di++)
                   {
                     // find any entry where we should put in the sequence being
                     // cross-referenced into the map
-                    jalview.datamodel.Mapping map = dbr[di].getMap();
+                    Mapping map = dbr[di].getMap();
                     if (map != null)
                     {
                       if (map.getTo() != null && map.getMap() != null)
                       {
-                        // should search the local dataset to find any existing
-                        // candidates for To !
+                        SequenceI matched = matcher
+                                .findIdMatch(map.getTo());
+                        if (matched != null)
+                        {
+                          /*
+                           * already got an xref to this sequence; update this
+                           * map to point to the same sequence, and add
+                           * any new dbrefs to it
+                           */
+                          for (DBRefEntry ref : map.getTo().getDBRefs())
+                          {
+                            matched.addDBRef(ref); // add or update mapping
+                          }
+                          map.setTo(matched);
+                        }
+                        else
+                        {
+                          matcher.add(map.getTo());
+                        }
                         try
                         {
                           // compare ms with dss and replace with dss in mapping
@@ -352,17 +358,55 @@ public class CrossRef
                           int sf = map.getMap().getToLowest();
                           int st = map.getMap().getToHighest();
                           SequenceI mappedrg = ms.getSubSequence(sf, st);
-                          SequenceI loc = dss.getSubSequence(sf, st);
+                          // SequenceI loc = dss.getSubSequence(sf, st);
                           if (mappedrg.getLength() > 0
-                                  && mappedrg.getSequenceAsString().equals(
-                                          loc.getSequenceAsString()))
+                                  && ms.getSequenceAsString().equals(
+                                          dss.getSequenceAsString()))
+                          // && mappedrg.getSequenceAsString().equals(
+                          // loc.getSequenceAsString()))
                           {
-                            System.err
-                                    .println("Mapping updated for retrieved crossreference");
+                            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.
                             map.setTo(dss);
+                            /*
+                             * copy sequence features as well, avoiding
+                             * duplication (e.g. same variation from 2 
+                             * transcripts)
+                             */
+                            SequenceFeature[] sfs = ms
+                                    .getSequenceFeatures();
+                            if (sfs != null)
+                            {
+                              for (SequenceFeature feat : sfs)
+                              {
+                                /* 
+                                 * we override SequenceFeature.equals here (but
+                                 * not elsewhere) to ignore Parent attribute
+                                 * TODO not quite working yet!
+                                 */
+                                if (!copiedFeatures
+                                        .contains(me.new MySequenceFeature(
+                                                feat)))
+                                {
+                                  dss.addSequenceFeature(feat);
+                                  copiedFeatures.add(feat);
+                                }
+                              }
+                            }
+                            cf.addMap(retrieved[rs].getDatasetSequence(),
+                                    dss, map.getMap());
+                          }
+                          // TODO remove this 'else' and the cf.addMap above?
+                          else
+                          {
+                            cf.addMap(retrieved[rs].getDatasetSequence(),
+                                    map.getTo(), map.getMap());
                           }
                         } catch (Exception e)
                         {
@@ -382,12 +426,12 @@ public class CrossRef
         }
       }
     }
+
+    Alignment ral = null;
     if (rseqs.size() > 0)
     {
-      SequenceI[] rsqs = new SequenceI[rseqs.size()];
-      rseqs.toArray(rsqs);
-      ral = new Alignment(rsqs);
-      if (cf != null && cf.getProtMappings() != null)
+      ral = new Alignment(rseqs.toArray(new SequenceI[rseqs.size()]));
+      if (cf != null && !cf.isEmpty())
       {
         ral.addCodonFrame(cf);
       }
@@ -396,6 +440,69 @@ public class CrossRef
   }
 
   /**
+   * Updates any empty mappings in the cross-references with one to a compatible
+   * retrieved sequence if found, and adds any new mappings to the
+   * AlignedCodonFrame
+   * 
+   * @param dna
+   * @param mapFrom
+   * @param xrefs
+   * @param retrieved
+   * @param acf
+   */
+  static void updateDbrefMappings(boolean dna, SequenceI mapFrom,
+          DBRefEntry[] xrefs, SequenceI[] retrieved, AlignedCodonFrame acf)
+  {
+    SequenceIdMatcher matcher = new SequenceIdMatcher(retrieved);
+    for (DBRefEntry xref : xrefs)
+    {
+      if (!xref.hasMap())
+      {
+        String targetSeqName = xref.getSource() + "|"
+                + xref.getAccessionId();
+        SequenceI[] matches = matcher.findAllIdMatches(targetSeqName);
+        if (matches == null)
+        {
+          return;
+        }
+        for (SequenceI seq : matches)
+        {
+          MapList mapping = null;
+          if (dna)
+          {
+            mapping = AlignmentUtils.mapCdnaToProtein(seq, mapFrom);
+          }
+          else
+          {
+            mapping = AlignmentUtils.mapCdnaToProtein(mapFrom, seq);
+            if (mapping != null)
+            {
+              mapping = mapping.getInverse();
+            }
+          }
+          if (mapping != null)
+          {
+            xref.setMap(new Mapping(seq, mapping));
+            if (dna)
+            {
+              AlignmentUtils.computeProteinFeatures(mapFrom, seq, mapping);
+            }
+            if (dna)
+            {
+              acf.addMap(mapFrom, seq, mapping);
+            }
+            else
+            {
+              acf.addMap(seq, mapFrom, mapping.getInverse());
+            }
+            continue;
+          }
+        }
+      }
+    }
+  }
+
+  /**
    * find references to lrfs in the cross-reference set of each sequence in
    * dataset (that is not equal to sequenceI) Identifies matching DBRefEntry
    * based on source and accession string only - Map and Version are nulled.
@@ -408,8 +515,7 @@ public class CrossRef
    */
   private static boolean searchDatasetXrefs(SequenceI sequenceI,
           boolean dna, DBRefEntry[] lrfs, AlignmentI dataset,
-          List<SequenceI> rseqs,
-          AlignedCodonFrame cf)
+          List<SequenceI> rseqs, AlignedCodonFrame cf)
   {
     boolean found = false;
     if (lrfs == null)
@@ -422,38 +528,26 @@ public class CrossRef
       // add in wildcards
       xref.setVersion(null);
       xref.setMap(null);
-      found = searchDataset(sequenceI, xref, dataset, rseqs, cf, false, dna);
+      found |= searchDataset(sequenceI, xref, dataset, rseqs, cf, false,
+              dna);
     }
     return found;
   }
 
   /**
-   * search a given sequence dataset for references matching cross-references to
-   * the given sequence
+   * Searches dataset for DBRefEntrys matching the given one (xrf) and adds the
+   * associated sequence to rseqs
    * 
    * @param sequenceI
+   *          a sequence to ignore (start point of search)
    * @param xrf
+   *          a cross-reference to try to match
    * @param dataset
+   *          sequences to search in
    * @param rseqs
-   *          set of unique sequences
+   *          result list to add to
    * @param cf
-   * @return true if one or more unique sequences were found and added
-   */
-  public static boolean searchDataset(SequenceI sequenceI, DBRefEntry xrf,
-          AlignmentI dataset, List<SequenceI> rseqs, AlignedCodonFrame cf)
-  {
-    return searchDataset(sequenceI, xrf, dataset, rseqs, cf, true, false);
-  }
-
-  /**
-   * TODO: generalise to different protein classifications Search dataset for
-   * DBRefEntrys matching the given one (xrf) and add the associated sequence to
-   * rseq.
-   * 
-   * @param sequenceI
-   * @param xrf
-   * @param dataset
-   * @param rseqs
+   *          a set of sequence mappings to add to
    * @param direct
    *          - search all references or only subset
    * @param dna
@@ -465,7 +559,6 @@ public class CrossRef
           boolean direct, boolean dna)
   {
     boolean found = false;
-    SequenceI[] typer = new SequenceI[1];
     if (dataset == null)
     {
       return false;
@@ -487,105 +580,82 @@ public class CrossRef
             System.err
                     .println("Implementation warning: getProducts passed a dataset alignment without dataset sequences in it!");
           }
-          if (nxt != sequenceI && nxt != sequenceI.getDatasetSequence())
+          if (nxt == sequenceI || nxt == sequenceI.getDatasetSequence())
           {
-            // check if this is the correct sequence type
+            continue;
+          }
+          // check if this is the correct sequence type
+          {
+            // TODO 'direct' is always set to false - remove?
+            // or should it be 'true' from findXrefSequences?
+            // also its Javadoc conflicts with its use:
+            // test below implies 'direct' means find complementary sequences,
+            // !direct means select same molecule type
+            boolean isDna = Comparison
+                    .isNucleotide(new SequenceI[] { nxt });
+            if ((direct && isDna == dna) || (!direct && isDna != dna))
             {
-              typer[0] = nxt;
-              boolean isDna = jalview.util.Comparison.isNucleotide(typer);
-              if ((direct && isDna == dna) || (!direct && isDna != dna))
-              {
-                // skip this sequence because it is same molecule type
-                continue;
-              }
+              // skip this sequence because it is wrong molecule type
+              continue;
             }
+          }
 
-            // look for direct or indirect references in common
-            DBRefEntry[] poss = nxt.getDBRef(), cands = null;
-            if (direct)
-            {
-              cands = jalview.util.DBRefUtils.searchRefs(poss, xrf);
-            }
-            else
-            {
-              poss = CrossRef.findXDbRefs(dna, poss); //
-              cands = jalview.util.DBRefUtils.searchRefs(poss, xrf);
-            }
-            if (cands != null)
+          // look for direct or indirect references in common
+          DBRefEntry[] poss = nxt.getDBRefs();
+          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(!dna, poss);
+            cands = DBRefUtils.searchRefs(poss, xrf);
+          }
+          if (cands != null)
+          {
+            if (!rseqs.contains(nxt))
             {
-              if (!rseqs.contains(nxt))
+              found = true;
+              rseqs.add(nxt);
+              boolean foundmap = cf != null;
+              // don't search if we aren't given a codon map object
+              for (int r = 0; foundmap && r < cands.length; r++)
               {
-                rseqs.add(nxt);
-                boolean foundmap = cf != null;
-                // don't search if we aren't given a codon map object
-                for (int r = 0; foundmap && r < cands.length; r++)
+                if (cands[r].hasMap())
                 {
-                  if (cands[r].hasMap())
+                  Mapping mapping = cands[r].getMap();
+                  MapList map = mapping.getMap();
+                  if (mapping.getTo() != null
+                          && map.getFromRatio() != map.getToRatio())
                   {
-                    if (cands[r].getMap().getTo() != null
-                            && cands[r].getMap().getMap().getFromRatio() != cands[r]
-                                    .getMap().getMap().getToRatio())
+                    // get sense of map correct for adding to product
+                    // alignment.
+                    if (dna)
                     {
-                      foundmap = true;
-                      // get sense of map correct for adding to product
-                      // alignment.
-                      if (dna)
-                      {
-                        // map is from dna seq to a protein product
-                        cf.addMap(sequenceI, nxt, cands[r].getMap()
-                                .getMap());
-                      }
-                      else
-                      {
-                        // map should be from protein seq to its coding dna
-                        cf.addMap(nxt, sequenceI, cands[r].getMap()
-                                .getMap().getInverse());
-                      }
+                      // map is from dna seq to a protein product
+                      cf.addMap(sequenceI, nxt, map);
+                    }
+                    else
+                    {
+                      // map should be from protein seq to its coding dna
+                      cf.addMap(nxt, sequenceI, map.getInverse());
                     }
                   }
                 }
-                // TODO: add mapping between sequences if necessary
-                found = true;
               }
+              // TODO: add mapping between sequences if necessary
             }
-
           }
         }
       }
     }
     return found;
   }
-
-  /**
-   * precalculate different products that can be found for seqs in dataset and
-   * return them.
-   * 
-   * @param dna
-   * @param seqs
-   * @param dataset
-   * @param fake
-   *          - don't actually build lists - just get types
-   * @return public static Object[] buildXProductsList(boolean dna, SequenceI[]
-   *         seqs, AlignmentI dataset, boolean fake) { String types[] =
-   *         jalview.analysis.CrossRef.findSequenceXrefTypes( dna, seqs,
-   *         dataset); if (types != null) { System.out.println("Xref Types for:
-   *         "+(dna ? "dna" : "prot")); for (int t = 0; t < types.length; t++) {
-   *         System.out.println("Type: " + types[t]); SequenceI[] prod =
-   *         jalview.analysis.CrossRef.findXrefSequences(seqs, dna, types[t]);
-   *         System.out.println("Found " + ((prod == null) ? "no" : "" +
-   *         prod.length) + " products"); if (prod!=null) { for (int p=0;
-   *         p<prod.length; p++) { System.out.println("Prod "+p+":
-   *         "+prod[p].getDisplayId(true)); } } } } else {
-   *         System.out.println("Trying getProducts for
-   *         "+al.getSequenceAt(0).getDisplayId(true));
-   *         System.out.println("Search DS Xref for: "+(dna ? "dna" : "prot"));
-   *         // have a bash at finding the products amongst all the retrieved
-   *         sequences. SequenceI[] prod =
-   *         jalview.analysis.CrossRef.findXrefSequences(al
-   *         .getSequencesArray(), dna, null, ds); System.out.println("Found " +
-   *         ((prod == null) ? "no" : "" + prod.length) + " products"); if
-   *         (prod!=null) { // select non-equivalent sequences from dataset list
-   *         for (int p=0; p<prod.length; p++) { System.out.println("Prod "+p+":
-   *         "+prod[p].getDisplayId(true)); } } } }
-   */
 }