+ * @param dataset
+ * - existing dataset. We check for sequences that look like the CDS
+ * we are about to construct, if one exists already, then we will
+ * just return that one.
+ * @return CDS sequence (as a dataset sequence)
+ */
+ static SequenceI makeCdsSequence(SequenceI seq, Mapping mapping,
+ AlignmentI dataset)
+ {
+ /*
+ * construct CDS sequence name as "CDS|" with 'from id' held in the mapping
+ * if set (e.g. EMBL protein_id), else sequence name appended
+ */
+ String mapFromId = mapping.getMappedFromId();
+ final String seqId = "CDS|"
+ + (mapFromId != null ? mapFromId : seq.getName());
+
+ SequenceI newSeq = null;
+
+ final MapList maplist = mapping.getMap();
+ if (maplist.isContiguous() && maplist.isFromForwardStrand())
+ {
+ /*
+ * just a subsequence, keep same dataset sequence
+ */
+ int start = maplist.getFromLowest();
+ int end = maplist.getFromHighest();
+ newSeq = seq.getSubSequence(start - 1, end);
+ newSeq.setName(seqId);
+ }
+ else
+ {
+ /*
+ * construct by splicing mapped from ranges
+ */
+ char[] seqChars = seq.getSequence();
+ List<int[]> fromRanges = maplist.getFromRanges();
+ int cdsWidth = MappingUtils.getLength(fromRanges);
+ char[] newSeqChars = new char[cdsWidth];
+
+ int newPos = 0;
+ for (int[] range : fromRanges)
+ {
+ if (range[0] <= range[1])
+ {
+ // forward strand mapping - just copy the range
+ int length = range[1] - range[0] + 1;
+ System.arraycopy(seqChars, range[0] - 1, newSeqChars, newPos,
+ length);
+ newPos += length;
+ }
+ else
+ {
+ // reverse strand mapping - copy and complement one by one
+ for (int i = range[0]; i >= range[1]; i--)
+ {
+ newSeqChars[newPos++] = Dna.getComplement(seqChars[i - 1]);
+ }
+ }
+ }
+
+ newSeq = new Sequence(seqId, newSeqChars, 1, newPos);
+ }
+
+ if (dataset != null)
+ {
+ SequenceI[] matches = dataset.findSequenceMatch(newSeq.getName());
+ if (matches != null)
+ {
+ boolean matched = false;
+ for (SequenceI mtch : matches)
+ {
+ if (mtch.getStart() != newSeq.getStart())
+ {
+ continue;
+ }
+ if (mtch.getEnd() != newSeq.getEnd())
+ {
+ continue;
+ }
+ if (!Arrays.equals(mtch.getSequence(), newSeq.getSequence()))
+ {
+ continue;
+ }
+ if (!matched)
+ {
+ matched = true;
+ newSeq = mtch;
+ }
+ else
+ {
+ System.err.println(
+ "JAL-2154 regression: warning - found (and ignnored a duplicate CDS sequence):"
+ + mtch.toString());
+ }
+ }
+ }
+ }
+ // newSeq.setDescription(mapFromId);
+
+ return newSeq;
+ }
+
+ /**
+ * Adds any DBRefEntrys to cdsSeq from contig that have a Mapping congruent to
+ * the given mapping.
+ *
+ * @param cdsSeq
+ * @param contig
+ * @param proteinProduct
+ * @param mapping
+ * @return list of DBRefEntrys added
+ */
+ protected static List<DBRefEntry> propagateDBRefsToCDS(SequenceI cdsSeq,
+ SequenceI contig, SequenceI proteinProduct, Mapping mapping)
+ {
+
+ // gather direct refs from contig congruent with mapping
+ List<DBRefEntry> direct = new ArrayList<>();
+ HashSet<String> directSources = new HashSet<>();
+
+ List<DBRefEntry> refs = contig.getDBRefs();
+ if (refs != null)
+ {
+ for (int ib = 0, nb = refs.size(); ib < nb; ib++)
+ {
+ DBRefEntry dbr = refs.get(ib);
+ MapList map;
+ if (dbr.hasMap() && (map = dbr.getMap().getMap()).isTripletMap())
+ {
+ // check if map is the CDS mapping
+ if (mapping.getMap().equals(map))
+ {
+ direct.add(dbr);
+ directSources.add(dbr.getSource());
+ }
+ }
+ }
+ }
+ List<DBRefEntry> onSource = DBRefUtils.selectRefs(
+ proteinProduct.getDBRefs(),
+ directSources.toArray(new String[0]));
+ List<DBRefEntry> propagated = new ArrayList<>();
+
+ // and generate appropriate mappings
+ for (int ic = 0, nc = direct.size(); ic < nc; ic++)
+ {
+ DBRefEntry cdsref = direct.get(ic);
+ Mapping m = cdsref.getMap();
+ // clone maplist and mapping
+ MapList cdsposmap = new MapList(
+ Arrays.asList(new int[][]
+ { new int[] { cdsSeq.getStart(), cdsSeq.getEnd() } }),
+ m.getMap().getToRanges(), 3, 1);
+ Mapping cdsmap = new Mapping(m.getTo(), m.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.
+ *
+ * @param fromSeq
+ * @param toSeq
+ * @param mapping
+ * the mapping from 'fromSeq' to 'toSeq'
+ * @param select
+ * if not null, only features of this type are copied (including
+ * subtypes in the Sequence Ontology)
+ * @param omitting