3 import jalview.analysis.AlignmentSorter;
4 import jalview.api.AlignViewportI;
5 import jalview.commands.CommandI;
6 import jalview.commands.EditCommand;
7 import jalview.commands.EditCommand.Action;
8 import jalview.commands.EditCommand.Edit;
9 import jalview.commands.OrderCommand;
10 import jalview.datamodel.AlignedCodonFrame;
11 import jalview.datamodel.AlignmentI;
12 import jalview.datamodel.AlignmentOrder;
13 import jalview.datamodel.SearchResults;
14 import jalview.datamodel.Sequence;
15 import jalview.datamodel.SequenceGroup;
16 import jalview.datamodel.SequenceI;
18 import java.util.ArrayList;
19 import java.util.HashMap;
20 import java.util.Iterator;
21 import java.util.List;
26 * Helper methods for manipulations involving sequence mappings.
31 public final class MappingUtils
35 * Helper method to map a CUT or PASTE command.
38 * the original command
40 * if true, the command is to be undone
42 * the mapped sequences to apply the mapped command to
44 * the mapped EditCommand to add to
47 protected static void mapCutOrPaste(Edit edit, boolean undo,
48 List<SequenceI> targetSeqs, EditCommand result,
49 Set<AlignedCodonFrame> mappings)
51 Action action = edit.getAction();
54 action = action.getUndoAction();
57 System.err.println("MappingUtils.mapCutOrPaste not yet implemented");
61 * Returns a new EditCommand representing the given command as mapped to the
62 * given sequences. If there is no mapping, returns null.
71 public static EditCommand mapEditCommand(EditCommand command,
72 boolean undo, final AlignmentI alignment, char gapChar,
73 Set<AlignedCodonFrame> mappings)
76 * Cache a copy of the target sequences so we can mimic successive edits on
77 * them. This lets us compute mappings for all edits in the set.
79 Map<SequenceI, SequenceI> targetCopies = new HashMap<SequenceI, SequenceI>();
80 for (SequenceI seq : alignment.getSequences())
82 SequenceI ds = seq.getDatasetSequence();
85 final SequenceI copy = new Sequence("", new String(
87 copy.setDatasetSequence(ds);
88 targetCopies.put(ds, copy);
93 * Compute 'source' sequences as they were before applying edits:
95 Map<SequenceI, SequenceI> originalSequences = command.priorState(undo);
97 EditCommand result = new EditCommand();
98 Iterator<Edit> edits = command.getEditIterator(!undo);
99 while (edits.hasNext())
101 Edit edit = edits.next();
102 if (edit.getAction() == Action.CUT
103 || edit.getAction() == Action.PASTE)
105 mapCutOrPaste(edit, undo, alignment.getSequences(), result,
108 else if (edit.getAction() == Action.INSERT_GAP
109 || edit.getAction() == Action.DELETE_GAP)
111 mapInsertOrDelete(edit, undo, originalSequences,
112 alignment.getSequences(),
113 targetCopies, gapChar, result, mappings);
116 return result.getSize() > 0 ? result : null;
120 * Helper method to map an edit command to insert or delete gaps.
123 * the original command
125 * if true, the action is to undo the command
126 * @param originalSequences
127 * the sequences the command acted on
129 * @param targetCopies
132 * the new EditCommand to add mapped commands to
135 protected static void mapInsertOrDelete(Edit edit, boolean undo,
136 Map<SequenceI, SequenceI> originalSequences,
137 final List<SequenceI> targetSeqs,
138 Map<SequenceI, SequenceI> targetCopies, char gapChar,
139 EditCommand result, Set<AlignedCodonFrame> mappings)
141 Action action = edit.getAction();
144 * Invert sense of action if an Undo.
148 action = action.getUndoAction();
150 final int count = edit.getNumber();
151 final int editPos = edit.getPosition();
152 for (SequenceI seq : edit.getSequences())
155 * Get residue position at (or to right of) edit location. Note we use our
156 * 'copy' of the sequence before editing for this.
158 SequenceI ds = seq.getDatasetSequence();
163 final SequenceI actedOn = originalSequences.get(ds);
164 final int seqpos = actedOn.findPosition(editPos);
167 * Determine all mappings from this position to mapped sequences.
169 SearchResults sr = buildSearchResults(seq, seqpos, mappings);
173 for (SequenceI targetSeq : targetSeqs)
175 ds = targetSeq.getDatasetSequence();
180 SequenceI copyTarget = targetCopies.get(ds);
181 final int[] match = sr.getResults(copyTarget, 0,
182 copyTarget.getLength());
185 final int ratio = 3; // TODO: compute this - how?
186 final int mappedCount = count * ratio;
189 * Shift Delete start position left, as it acts on positions to its
192 int mappedEditPos = action == Action.DELETE_GAP ? match[0]
193 - mappedCount : match[0];
194 Edit e = result.new Edit(action, new SequenceI[]
195 { targetSeq }, mappedEditPos, mappedCount, gapChar);
199 * and 'apply' the edit to our copy of its target sequence
201 if (action == Action.INSERT_GAP)
203 copyTarget.setSequence(new String(StringUtils.insertCharAt(
204 copyTarget.getSequence(), mappedEditPos, mappedCount,
207 else if (action == Action.DELETE_GAP)
209 copyTarget.setSequence(new String(StringUtils.deleteChars(
210 copyTarget.getSequence(), mappedEditPos,
211 mappedEditPos + mappedCount)));
217 * and 'apply' the edit to our copy of its source sequence
219 if (action == Action.INSERT_GAP)
221 actedOn.setSequence(new String(StringUtils.insertCharAt(
222 actedOn.getSequence(), editPos, count, gapChar)));
224 else if (action == Action.DELETE_GAP)
226 actedOn.setSequence(new String(StringUtils.deleteChars(
227 actedOn.getSequence(), editPos, editPos + count)));
233 * Returns a SearchResults object describing the mapped region corresponding
234 * to the specified sequence position.
241 public static SearchResults buildSearchResults(SequenceI seq, int index,
242 Set<AlignedCodonFrame> seqmappings)
244 SearchResults results;
245 results = new SearchResults();
246 if (index >= seq.getStart() && index <= seq.getEnd())
248 for (AlignedCodonFrame acf : seqmappings)
250 acf.markMappedRegion(seq, index, results);
252 results.addResult(seq, index, index);
258 * Returns a (possibly empty) SequenceGroup containing any sequences the
259 * mapped viewport corresponding to the given group in the source viewport.
266 public static SequenceGroup mapSequenceGroup(SequenceGroup sg,
267 AlignViewportI av, AlignViewportI mapped)
270 * Map sequence selection. Note the SequenceGroup holds aligned sequences,
271 * the mappings hold dataset sequences.
273 AlignedCodonFrame[] codonFrames = av.getAlignment()
277 * Copy group name, colours, but not sequences
279 SequenceGroup mappedGroup = new SequenceGroup(sg);
281 // TODO set width of mapped group
283 for (SequenceI selected : sg.getSequences())
285 for (AlignedCodonFrame acf : codonFrames)
287 SequenceI dnaSeq = acf.getDnaForAaSeq(selected);
290 for (SequenceI seq : mapped.getAlignment().getSequences())
292 if (seq.getDatasetSequence() == dnaSeq)
294 mappedGroup.addSequence(seq, false);
305 * Returns an OrderCommand equivalent to the given one, but acting on mapped
306 * sequences as described by the mappings, or null if no mapping can be made.
309 * the original order command
311 * if true, the action is to undo the sort
313 * the alignment we are mapping to
315 * the mappings available
318 public static CommandI mapOrderCommand(OrderCommand command,
319 boolean undo, AlignmentI mapTo,
320 Set<AlignedCodonFrame> mappings)
322 SequenceI[] sortOrder = command.getSequenceOrder(undo);
323 List<SequenceI> mappedOrder = new ArrayList<SequenceI>();
325 for (SequenceI seq : sortOrder)
327 for (AlignedCodonFrame acf : mappings)
329 SequenceI dnaSeq = acf.getDnaForAaSeq(seq);
332 for (SequenceI seq2 : mapTo.getSequences())
334 if (seq2.getDatasetSequence() == dnaSeq)
336 mappedOrder.add(seq2);
346 * Return null if no mappings made.
354 * Add any unmapped sequences on the end of the sort in their original
357 if (j < mapTo.getHeight())
359 for (SequenceI seq : mapTo.getSequences())
361 if (!mappedOrder.contains(seq))
363 mappedOrder.add(seq);
369 * Have to align the sequences before constructing the OrderCommand - which
370 * then realigns them?!?
372 final SequenceI[] mappedOrderArray = mappedOrder
373 .toArray(new SequenceI[mappedOrder.size()]);
374 SequenceI[] oldOrder = mapTo.getSequencesArray();
375 AlignmentSorter.sortBy(mapTo, new AlignmentOrder(mappedOrderArray));
376 final OrderCommand result = new OrderCommand(command.getDescription(),