+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ *
+ * This file is part of Jalview.
+ *
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * Jalview is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
package jalview.util;
import jalview.analysis.AlignmentSorter;
import jalview.datamodel.SequenceI;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Set;
/**
* Helper methods for manipulations involving sequence mappings.
*/
protected static void mapCutOrPaste(Edit edit, boolean undo,
List<SequenceI> targetSeqs, EditCommand result,
- Set<AlignedCodonFrame> mappings)
+ List<AlignedCodonFrame> mappings)
{
Action action = edit.getAction();
if (undo)
*/
public static EditCommand mapEditCommand(EditCommand command,
boolean undo, final AlignmentI mapTo, char gapChar,
- Set<AlignedCodonFrame> mappings)
+ List<AlignedCodonFrame> mappings)
{
/*
* For now, only support mapping from protein edits to cDna
Map<SequenceI, SequenceI> originalSequences,
final List<SequenceI> targetSeqs,
Map<SequenceI, SequenceI> targetCopies, char gapChar,
- EditCommand result, Set<AlignedCodonFrame> mappings)
+ EditCommand result, List<AlignedCodonFrame> mappings)
{
Action action = edit.getAction();
*/
int mappedEditPos = action == Action.DELETE_GAP ? match[0]
- mappedCount : match[0];
- Edit e = result.new Edit(action, new SequenceI[]
- { targetSeq }, mappedEditPos, mappedCount, gapChar);
+ Edit e = result.new Edit(action, new SequenceI[] { targetSeq },
+ mappedEditPos, mappedCount, gapChar);
result.addEdit(e);
/*
* @return
*/
public static SearchResults buildSearchResults(SequenceI seq, int index,
- Set<AlignedCodonFrame> seqmappings)
+ List<AlignedCodonFrame> seqmappings)
+ {
+ SearchResults results = new SearchResults();
+ addSearchResults(results, seq, index, seqmappings);
+ return results;
+ }
+
+ /**
+ * Adds entries to a SearchResults object describing the mapped region
+ * corresponding to the specified sequence position.
+ *
+ * @param results
+ * @param seq
+ * @param index
+ * @param seqmappings
+ */
+ public static void addSearchResults(SearchResults results, SequenceI seq,
+ int index, List<AlignedCodonFrame> seqmappings)
{
- SearchResults results;
- results = new SearchResults();
if (index >= seq.getStart() && index <= seq.getEnd())
{
for (AlignedCodonFrame acf : seqmappings)
acf.markMappedRegion(seq, index, results);
}
}
- return results;
}
/**
* @param mapTo
* @return
*/
- public static SequenceGroup mapSequenceGroup(SequenceGroup sg,
- AlignViewportI mapFrom, AlignViewportI mapTo)
+ public static SequenceGroup mapSequenceGroup(final SequenceGroup sg,
+ final AlignViewportI mapFrom, final AlignViewportI mapTo)
{
/*
* Note the SequenceGroup holds aligned sequences, the mappings hold dataset
*/
boolean targetIsNucleotide = mapTo.isNucleotide();
AlignViewportI protein = targetIsNucleotide ? mapFrom : mapTo;
- Set<AlignedCodonFrame> codonFrames = protein.getAlignment()
+ List<AlignedCodonFrame> codonFrames = protein.getAlignment()
.getCodonFrames();
-
/*
- * Copy group name, name colours, but not sequences or sequence colour
- * scheme
+ * Copy group name, colours etc, but not sequences or sequence colour scheme
*/
SequenceGroup mappedGroup = new SequenceGroup(sg);
mappedGroup.cs = mapTo.getGlobalColourScheme();
mappedGroup.clear();
- // TODO set width of mapped group
+ int minStartCol = -1;
+ int maxEndCol = -1;
+ final int selectionStartRes = sg.getStartRes();
+ final int selectionEndRes = sg.getEndRes();
for (SequenceI selected : sg.getSequences())
{
+ /*
+ * Find the widest range of non-gapped positions in the selection range
+ */
+ int firstUngappedPos = selectionStartRes;
+ while (firstUngappedPos <= selectionEndRes
+ && Comparison.isGap(selected.getCharAt(firstUngappedPos)))
+ {
+ firstUngappedPos++;
+ }
+
+ /*
+ * If this sequence is only gaps in the selected range, skip it
+ */
+ if (firstUngappedPos > selectionEndRes)
+ {
+ continue;
+ }
+
+ int lastUngappedPos = selectionEndRes;
+ while (lastUngappedPos >= selectionStartRes
+ && Comparison.isGap(selected.getCharAt(lastUngappedPos)))
+ {
+ lastUngappedPos--;
+ }
+
+ /*
+ * Find the selected start/end residue positions in sequence
+ */
+ int startResiduePos = selected.findPosition(firstUngappedPos);
+ int endResiduePos = selected.findPosition(lastUngappedPos);
+
for (AlignedCodonFrame acf : codonFrames)
{
SequenceI mappedSequence = targetIsNucleotide ? acf
{
for (SequenceI seq : mapTo.getAlignment().getSequences())
{
+ int mappedStartResidue = 0;
+ int mappedEndResidue = 0;
if (seq.getDatasetSequence() == mappedSequence)
{
+ /*
+ * Found a sequence mapping. Locate the start/end mapped residues.
+ */
+ List<AlignedCodonFrame> mapping = Arrays.asList(new AlignedCodonFrame[] { acf });
+ SearchResults sr = buildSearchResults(selected,
+ startResiduePos, mapping);
+ for (Match m : sr.getResults())
+ {
+ mappedStartResidue = m.getStart();
+ mappedEndResidue = m.getEnd();
+ }
+ sr = buildSearchResults(selected, endResiduePos, mapping);
+ for (Match m : sr.getResults())
+ {
+ mappedStartResidue = Math.min(mappedStartResidue,
+ m.getStart());
+ mappedEndResidue = Math.max(mappedEndResidue, m.getEnd());
+ }
+
+ /*
+ * Find the mapped aligned columns, save the range. Note findIndex
+ * returns a base 1 position, SequenceGroup uses base 0
+ */
+ int mappedStartCol = seq.findIndex(mappedStartResidue) - 1;
+ minStartCol = minStartCol == -1 ? mappedStartCol : Math.min(
+ minStartCol, mappedStartCol);
+ int mappedEndCol = seq.findIndex(mappedEndResidue) - 1;
+ maxEndCol = maxEndCol == -1 ? mappedEndCol : Math.max(
+ maxEndCol, mappedEndCol);
mappedGroup.addSequence(seq, false);
break;
}
}
}
}
+ mappedGroup.setStartRes(minStartCol < 0 ? 0 : minStartCol);
+ mappedGroup.setEndRes(maxEndCol < 0 ? 0 : maxEndCol);
return mappedGroup;
}
* @return
*/
public static CommandI mapOrderCommand(OrderCommand command,
- boolean undo, AlignmentI mapTo, Set<AlignedCodonFrame> mappings)
+ boolean undo, AlignmentI mapTo, List<AlignedCodonFrame> mappings)
{
SequenceI[] sortOrder = command.getSequenceOrder(undo);
List<SequenceI> mappedOrder = new ArrayList<SequenceI>();
int j = 0;
+
+ /*
+ * Assumption: we are only interested in a cDNA/protein mapping; refactor in
+ * future if we want to support sorting (c)dna as (c)dna or protein as
+ * protein
+ */
+ boolean mappingToNucleotide = mapTo.isNucleotide();
for (SequenceI seq : sortOrder)
{
for (AlignedCodonFrame acf : mappings)
{
- /*
- * Try protein-to-Dna, failing that try dna-to-protein
- */
- SequenceI mappedSeq = acf.getDnaForAaSeq(seq);
- if (mappedSeq == null)
- {
- mappedSeq = acf.getAaForDnaSeq(seq);
- }
+ SequenceI mappedSeq = mappingToNucleotide ? acf.getDnaForAaSeq(seq)
+ : acf.getAaForDnaSeq(seq);
if (mappedSeq != null)
{
for (SequenceI seq2 : mapTo.getSequences())
{
boolean targetIsNucleotide = mapTo.isNucleotide();
AlignViewportI protein = targetIsNucleotide ? mapFrom : mapTo;
- Set<AlignedCodonFrame> codonFrames = protein.getAlignment()
+ List<AlignedCodonFrame> codonFrames = protein.getAlignment()
.getCodonFrames();
ColumnSelection mappedColumns = new ColumnSelection();
+
+ if (colsel == null)
+ {
+ return mappedColumns;
+ }
+
char fromGapChar = mapFrom.getAlignment().getGapCharacter();
// FIXME allow for hidden columns
}
/**
- * Returns the mapped codon for a given aligned sequence column position (base
- * 0).
+ * Returns the mapped codon or codons for a given aligned sequence column
+ * position (base 0).
*
* @param seq
* an aligned peptide sequence
* an aligned column position (base 0)
* @param mappings
* a set of codon mappings
- * @return the bases of the mapped codon in the cDNA dataset sequence, or null
- * if not found
+ * @return the bases of the mapped codon(s) in the cDNA dataset sequence(s),
+ * or an empty list if none found
*/
- public static char[] findCodonFor(SequenceI seq, int col,
- Set<AlignedCodonFrame> mappings)
+ public static List<char[]> findCodonsFor(SequenceI seq, int col,
+ List<AlignedCodonFrame> mappings)
{
+ List<char[]> result = new ArrayList<char[]>();
int dsPos = seq.findPosition(col);
for (AlignedCodonFrame mapping : mappings)
{
if (mapping.involvesSequence(seq))
{
- return mapping.getMappedCodon(seq.getDatasetSequence(), dsPos);
+ List<char[]> codons = mapping.getMappedCodons(
+ seq.getDatasetSequence(), dsPos);
+ if (codons != null)
+ {
+ result.addAll(codons);
+ }
}
}
- return null;
+ return result;
}
/**
- * Converts a series of [start, end] ranges into an array of individual
- * positions.
+ * Converts a series of [start, end] range pairs into an array of individual
+ * positions. This also caters for 'reverse strand' (start > end) cases.
*
* @param ranges
* @return
int count = 0;
for (int i = 0; i < ranges.length - 1; i += 2)
{
- count += ranges[i + 1] - ranges[i] + 1;
+ count += Math.abs(ranges[i + 1] - ranges[i]) + 1;
}
int[] result = new int[count];
int k = 0;
for (int i = 0; i < ranges.length - 1; i += 2)
{
- for (int j = ranges[i]; j <= ranges[i + 1]; j++)
+ int from = ranges[i];
+ final int to = ranges[i + 1];
+ int step = from <= to ? 1 : -1;
+ do
+ {
+ result[k++] = from;
+ from += step;
+ } while (from != to + step);
+ }
+ return result;
+ }
+
+ /**
+ * Returns a list of any mappings that are from or to the given (aligned or
+ * dataset) sequence.
+ *
+ * @param sequence
+ * @param mappings
+ * @return
+ */
+ public static List<AlignedCodonFrame> findMappingsForSequence(
+ SequenceI sequence, List<AlignedCodonFrame> mappings)
+ {
+ List<AlignedCodonFrame> result = new ArrayList<AlignedCodonFrame>();
+ if (sequence == null || mappings == null)
+ {
+ return result;
+ }
+ for (AlignedCodonFrame mapping : mappings)
+ {
+ if (mapping.involvesSequence(sequence))
{
- result[k++] = j;
+ result.add(mapping);
}
}
return result;