JAL-2154 JAL-2106 transfer primary refs to CDS for makeCDS
[jalview.git] / src / jalview / analysis / AlignmentUtils.java
index ec5b6a1..cc80384 100644 (file)
@@ -1700,7 +1700,8 @@ public class AlignmentUtils
           MapList cdsToProteinMap = new MapList(cdsRange, mapList.getToRanges(),
                   mapList.getFromRatio(), mapList.getToRatio());
           AlignedCodonFrame cdsToProteinMapping = new AlignedCodonFrame();
-          cdsToProteinMapping.addMap(cdsSeq, proteinProduct, cdsToProteinMap);
+          cdsToProteinMapping.addMap(cdsSeqDss, proteinProduct,
+                  cdsToProteinMap);
 
           /*
            * guard against duplicating the mapping if repeating this action
@@ -1710,23 +1711,8 @@ public class AlignmentUtils
             mappings.add(cdsToProteinMapping);
           }
 
-          /*
-           * copy protein's dbrefs to CDS sequence
-           * this enables Get Cross-References from CDS alignment
-           */
-          DBRefEntry[] proteinRefs = DBRefUtils.selectDbRefs(false,
-                  proteinProduct.getDBRefs());
-          if (proteinRefs != null)
-          {
-            for (DBRefEntry ref : proteinRefs)
-            {
-              DBRefEntry cdsToProteinRef = new DBRefEntry(ref);
-              cdsToProteinRef.setMap(new Mapping(proteinProduct,
-                      cdsToProteinMap));
-              cdsSeqDss.addDBRef(cdsToProteinRef);
-            }
-          }
-
+          propagateDBRefsToCDS(cdsSeqDss, dnaSeq.getDatasetSequence(),
+                  proteinProduct, aMapping);
           /*
            * add another mapping from original 'from' range to CDS
            */
@@ -1734,7 +1720,7 @@ public class AlignmentUtils
           MapList dnaToCdsMap = new MapList(mapList.getFromRanges(),
                   cdsRange, 1,
                   1);
-          dnaToCdsMapping.addMap(dnaSeq.getDatasetSequence(), cdsSeq,
+          dnaToCdsMapping.addMap(dnaSeq.getDatasetSequence(), cdsSeqDss,
                   dnaToCdsMap);
           if (!mappings.contains(dnaToCdsMapping))
           {
@@ -1748,28 +1734,41 @@ public class AlignmentUtils
            * same source and accession, so need a different accession for
            * the CDS from the dna sequence
            */
+          
           // specific use case:
           // Genomic contig ENSCHR:1, contains coding regions for ENSG01,
           // ENSG02, ENSG03, with transcripts and products similarly named.
           // cannot add distinct dbrefs mapping location on ENSCHR:1 to ENSG01
+          
           // JBPNote: ?? can't actually create an example that demonstrates we
           // need to
           // synthesize an xref.
-          // TODO: merge conflicts from JAL-2154 branch and use PrimaryDBRefs()
-          // for (DBRefEntry primRef:dnaDss.getPrimaryDBRefs())
-          // {
-          // creates a complementary cross-reference to the source sequence's
-          // primary reference.
-
-          // // problem here is that the cross-reference is synthesized -
-          // cdsSeq.getName() may be like 'CDS|dnaaccession' or 'CDS|emblcdsacc'
-          // // assuming cds version same as dna ?!?
-          // DBRefEntry proteinToCdsRef = new DBRefEntry(dnaRef.getSource(),
-          // dnaRef.getVersion(), cdsSeq.getName());
-          // proteinToCdsRef.setMap(new Mapping(cdsSeqDss, cdsToProteinMap
-          // .getInverse()));
-          // proteinProduct.addDBRef(proteinToCdsRef);
-          // }
+          
+          for (DBRefEntry primRef : dnaDss.getPrimaryDBRefs())
+          {
+            // creates a complementary cross-reference to the source sequence's
+            // primary reference.
+
+            DBRefEntry cdsCrossRef = new DBRefEntry(primRef.getSource(),
+                    primRef.getSource() + ":" + primRef.getVersion(),
+                    primRef.getAccessionId());
+            cdsCrossRef
+                    .setMap(new Mapping(dnaDss, new MapList(dnaToCdsMap)));
+            cdsSeqDss.addDBRef(cdsCrossRef);
+
+            // problem here is that the cross-reference is synthesized -
+            // cdsSeq.getName() may be like 'CDS|dnaaccession' or
+            // 'CDS|emblcdsacc'
+            // assuming cds version same as dna ?!?
+
+            DBRefEntry proteinToCdsRef = new DBRefEntry(
+                    primRef.getSource(), primRef.getVersion(),
+                    cdsSeq.getName());
+            //
+            proteinToCdsRef.setMap(new Mapping(cdsSeqDss, cdsToProteinMap
+                    .getInverse()));
+            proteinProduct.addDBRef(proteinToCdsRef);
+          }
 
           /*
            * transfer any features on dna that overlap the CDS
@@ -1900,7 +1899,7 @@ public class AlignmentUtils
         }
       }
     }
-
+    
     /*
      * assign 'from id' held in the mapping if set (e.g. EMBL protein_id),
      * else generate a sequence name
@@ -1914,6 +1913,84 @@ public class AlignmentUtils
   }
 
   /**
+   * add any DBRefEntrys to cdsSeq from contig that have a Mapping congruent to
+   * the given mapping.
+   * 
+   * @param cdsSeq
+   * @param contig
+   * @param mapping
+   * @return list of DBRefEntrys added.
+   */
+  public static List<DBRefEntry> propagateDBRefsToCDS(SequenceI cdsSeq,
+          SequenceI contig, SequenceI proteinProduct, Mapping mapping)
+  {
+
+    // gather direct refs from contig congrent with mapping
+    List<DBRefEntry> direct = new ArrayList<DBRefEntry>();
+    HashSet<String> directSources = new HashSet<String>();
+    if (contig.getDBRefs() != null)
+    {
+      for (DBRefEntry dbr : contig.getDBRefs())
+      {
+        if (dbr.hasMap() && dbr.getMap().getMap().isTripletMap())
+        {
+          MapList map = dbr.getMap().getMap();
+          // check if map is the CDS mapping
+          if (mapping.getMap().equals(map))
+          {
+            direct.add(dbr);
+            directSources.add(dbr.getSource());
+          }
+        }
+      }
+    }
+    DBRefEntry[] onSource = DBRefUtils.selectRefs(
+            proteinProduct.getDBRefs(),
+            directSources.toArray(new String[0]));
+    List<DBRefEntry> propagated = new ArrayList<DBRefEntry>();
+
+    // and generate appropriate mappings
+    for (DBRefEntry cdsref : direct)
+    {
+      // clone maplist and mapping
+      MapList cdsposmap = new MapList(Arrays.asList(new int[][] { new int[]
+      { cdsSeq.getStart(), cdsSeq.getEnd() } }), cdsref.getMap().getMap()
+              .getToRanges(), 3, 1);
+      Mapping cdsmap = new Mapping(cdsref.getMap().getTo(), cdsref.getMap()
+              .getMap());
+
+      // create dbref
+      DBRefEntry newref = new DBRefEntry(cdsref.getSource(),
+              cdsref.getVersion(), cdsref.getAccessionId(), new Mapping(
+                      cdsmap.getTo(), cdsposmap));
+
+      // and see if we can map to the protein product for this mapping.
+      // onSource is the filtered set of accessions on protein that we are
+      // tranferring, so we assume accession is the same.
+      if (cdsmap.getTo() == null && onSource != null)
+      {
+        List<DBRefEntry> sourceRefs = DBRefUtils.searchRefs(onSource,
+                cdsref.getAccessionId());
+        if (sourceRefs != null)
+        {
+          for (DBRefEntry srcref : sourceRefs)
+          {
+            if (srcref.getSource().equalsIgnoreCase(cdsref.getSource()))
+            {
+              // we have found a complementary dbref on the protein product, so
+              // update mapping's getTo
+              newref.getMap().setTo(proteinProduct);
+            }
+          }
+        }
+      }
+      cdsSeq.addDBRef(newref);
+      propagated.add(newref);
+    }
+    return propagated;
+  }
+
+  /**
    * Transfers co-located features on 'fromSeq' to 'toSeq', adjusting the
    * feature start/end ranges, optionally omitting specified feature types.
    * Returns the number of features copied.
@@ -2649,13 +2726,16 @@ public class AlignmentUtils
       return false; // should only pass alignments with datasets here
     }
 
-    // map from dataset sequence to alignment sequence
-    Map<SequenceI, SequenceI> alignedDatasets = new HashMap<SequenceI, SequenceI>();
+    // map from dataset sequence to alignment sequence(s)
+    Map<SequenceI, List<SequenceI>> alignedDatasets = new HashMap<SequenceI, List<SequenceI>>();
     for (SequenceI seq : aligned.getSequences())
     {
-      // JAL-2110: fail if two or more alignment sequences have a common dataset
-      // sequence.
-      alignedDatasets.put(seq.getDatasetSequence(), seq);
+      SequenceI ds = seq.getDatasetSequence();
+      if (alignedDatasets.get(ds) == null)
+      {
+        alignedDatasets.put(ds, new ArrayList<SequenceI>());
+      }
+      alignedDatasets.get(ds).add(seq);
     }
 
     /*
@@ -2671,17 +2751,22 @@ public class AlignmentUtils
     }
 
     /*
-     * second pass - copy aligned sequences
+     * second pass - copy aligned sequences;
+     * heuristic rule: pair off sequences in order for the case where 
+     * more than one shares the same dataset sequence 
      */
     for (SequenceI seq : unaligned.getSequences())
     {
-      SequenceI alignedSequence = alignedDatasets.get(seq
+      List<SequenceI> alignedSequences = alignedDatasets.get(seq
               .getDatasetSequence());
-      // JAL-2110: fail if two or more alignment sequences have common dataset
-      // sequence.
       // TODO: getSequenceAsString() will be deprecated in the future
       // TODO: need to leave to SequenceI implementor to update gaps
-      seq.setSequence(alignedSequence.getSequenceAsString());
+      seq.setSequence(alignedSequences.get(0).getSequenceAsString());
+      if (alignedSequences.size() > 0)
+      {
+        // pop off aligned sequences (except the last one)
+        alignedSequences.remove(0);
+      }
     }
 
     return true;