2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3 * Copyright (C) 2014 The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.structure;
23 import jalview.analysis.AlignSeq;
24 import jalview.api.StructureSelectionManagerProvider;
25 import jalview.commands.CommandI;
26 import jalview.commands.EditCommand;
27 import jalview.commands.EditCommand.Action;
28 import jalview.commands.EditCommand.Edit;
29 import jalview.datamodel.AlignedCodonFrame;
30 import jalview.datamodel.AlignmentAnnotation;
31 import jalview.datamodel.Annotation;
32 import jalview.datamodel.PDBEntry;
33 import jalview.datamodel.SearchResults;
34 import jalview.datamodel.Sequence;
35 import jalview.datamodel.SequenceI;
36 import jalview.io.AppletFormatAdapter;
37 import jalview.util.MessageManager;
38 import jalview.util.StringUtils;
40 import java.io.PrintStream;
41 import java.util.ArrayList;
42 import java.util.Enumeration;
43 import java.util.HashMap;
44 import java.util.IdentityHashMap;
45 import java.util.Iterator;
46 import java.util.LinkedHashSet;
47 import java.util.List;
50 import java.util.Vector;
53 import MCview.PDBChain;
55 public class StructureSelectionManager
57 static IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> instances;
59 StructureMapping[] mappings;
61 private boolean processSecondaryStructure = false;
63 private boolean secStructServices = false;
65 private boolean addTempFacAnnot = false;
67 private Set<AlignedCodonFrame> seqmappings = new LinkedHashSet<AlignedCodonFrame>();
69 private List<CommandListener> commandListeners = new ArrayList<CommandListener>();
72 * @return true if will try to use external services for processing secondary
75 public boolean isSecStructServices()
77 return secStructServices;
81 * control use of external services for processing secondary structure
83 * @param secStructServices
85 public void setSecStructServices(boolean secStructServices)
87 this.secStructServices = secStructServices;
91 * flag controlling addition of any kind of structural annotation
93 * @return true if temperature factor annotation will be added
95 public boolean isAddTempFacAnnot()
97 return addTempFacAnnot;
101 * set flag controlling addition of structural annotation
103 * @param addTempFacAnnot
105 public void setAddTempFacAnnot(boolean addTempFacAnnot)
107 this.addTempFacAnnot = addTempFacAnnot;
112 * @return if true, the structure manager will attempt to add secondary
113 * structure lines for unannotated sequences
116 public boolean isProcessSecondaryStructure()
118 return processSecondaryStructure;
122 * Control whether structure manager will try to annotate mapped sequences
123 * with secondary structure from PDB data.
127 public void setProcessSecondaryStructure(boolean enable)
129 processSecondaryStructure = enable;
133 * debug function - write all mappings to stdout
135 public void reportMapping()
137 if (mappings == null)
139 System.err.println("reportMapping: No PDB/Sequence mappings.");
143 System.err.println("reportMapping: There are " + mappings.length
145 for (int m = 0; m < mappings.length; m++)
147 System.err.println("mapping " + m + " : " + mappings[m].pdbfile);
153 * map between the PDB IDs (or structure identifiers) used by Jalview and the
154 * absolute filenames for PDB data that corresponds to it
156 HashMap<String, String> pdbIdFileName = new HashMap<String, String>(),
157 pdbFileNameId = new HashMap<String, String>();
159 public void registerPDBFile(String idForFile, String absoluteFile)
161 pdbIdFileName.put(idForFile, absoluteFile);
162 pdbFileNameId.put(absoluteFile, idForFile);
165 public String findIdForPDBFile(String idOrFile)
167 String id = pdbFileNameId.get(idOrFile);
171 public String findFileForPDBId(String idOrFile)
173 String id = pdbIdFileName.get(idOrFile);
177 public boolean isPDBFileRegistered(String idOrFile)
179 return pdbFileNameId.containsKey(idOrFile)
180 || pdbIdFileName.containsKey(idOrFile);
183 private static StructureSelectionManager nullProvider = null;
185 public static StructureSelectionManager getStructureSelectionManager(
186 StructureSelectionManagerProvider context)
190 if (nullProvider == null)
192 if (instances != null)
194 throw new Error(MessageManager.getString("error.implementation_error_structure_selection_manager_null"),
195 new NullPointerException(MessageManager.getString("exception.ssm_context_is_null")));
199 nullProvider = new StructureSelectionManager();
204 if (instances == null)
206 instances = new java.util.IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager>();
208 StructureSelectionManager instance = instances.get(context);
209 if (instance == null)
211 if (nullProvider != null)
213 instance = nullProvider;
217 instance = new StructureSelectionManager();
219 instances.put(context, instance);
225 * flag controlling whether SeqMappings are relayed from received sequence
226 * mouse over events to other sequences
228 boolean relaySeqMappings = true;
231 * Enable or disable relay of seqMapping events to other sequences. You might
232 * want to do this if there are many sequence mappings and the host computer
237 public void setRelaySeqMappings(boolean relay)
239 relaySeqMappings = relay;
243 * get the state of the relay seqMappings flag.
245 * @return true if sequence mouse overs are being relayed to other mapped
248 public boolean isRelaySeqMappingsEnabled()
250 return relaySeqMappings;
253 Vector listeners = new Vector();
256 * register a listener for alignment sequence mouseover events
260 public void addStructureViewerListener(Object svl)
262 if (!listeners.contains(svl))
264 listeners.addElement(svl);
268 public String alreadyMappedToFile(String pdbid)
270 if (mappings != null)
272 for (int i = 0; i < mappings.length; i++)
274 if (mappings[i].getPdbId().equals(pdbid))
276 return mappings[i].pdbfile;
284 * Import structure data and register a structure mapping for broadcasting
285 * colouring, mouseovers and selection events (convenience wrapper).
288 * - one or more sequences to be mapped to pdbFile
289 * @param targetChains
290 * - optional chain specification for mapping each sequence to pdb
291 * (may be nill, individual elements may be nill)
293 * - structure data resource
295 * - how to resolve data from resource
296 * @return null or the structure data parsed as a pdb file
298 synchronized public MCview.PDBfile setMapping(SequenceI[] sequence,
299 String[] targetChains, String pdbFile, String protocol)
301 return setMapping(true, sequence, targetChains, pdbFile, protocol);
305 * create sequence structure mappings between each sequence and the given
306 * pdbFile (retrieved via the given protocol).
308 * @param forStructureView
309 * when true, record the mapping for use in mouseOvers
312 * - one or more sequences to be mapped to pdbFile
313 * @param targetChains
314 * - optional chain specification for mapping each sequence to pdb
315 * (may be nill, individual elements may be nill)
317 * - structure data resource
319 * - how to resolve data from resource
320 * @return null or the structure data parsed as a pdb file
322 synchronized public MCview.PDBfile setMapping(boolean forStructureView,
323 SequenceI[] sequence,
324 String[] targetChains, String pdbFile, String protocol)
327 * There will be better ways of doing this in the future, for now we'll use
328 * the tried and tested MCview pdb mapping
330 MCview.PDBfile pdb = null;
331 boolean parseSecStr = processSecondaryStructure;
332 if (isPDBFileRegistered(pdbFile))
334 for (SequenceI sq : sequence)
337 while (ds.getDatasetSequence() != null)
339 ds = ds.getDatasetSequence();
342 if (ds.getAnnotation() != null)
344 for (AlignmentAnnotation ala : ds.getAnnotation())
346 // false if any annotation present from this structure
347 // JBPNote this fails for jmol/chimera view because the *file* is
348 // passed, not the structure data ID -
349 if (MCview.PDBfile.isCalcIdForFile(ala,
350 findIdForPDBFile(pdbFile)))
360 pdb = new MCview.PDBfile(addTempFacAnnot, parseSecStr,
361 secStructServices, pdbFile, protocol);
362 if (pdb.id != null && pdb.id.trim().length() > 0
363 && AppletFormatAdapter.FILE.equals(protocol))
365 registerPDBFile(pdb.id.trim(), pdbFile);
367 } catch (Exception ex)
369 ex.printStackTrace();
374 for (int s = 0; s < sequence.length; s++)
376 boolean infChain = true;
377 if (targetChains != null && targetChains[s] != null)
380 targetChain = targetChains[s];
382 else if (sequence[s].getName().indexOf("|") > -1)
384 targetChain = sequence[s].getName().substring(
385 sequence[s].getName().lastIndexOf("|") + 1);
386 if (targetChain.length() > 1)
388 if (targetChain.trim().length() == 0)
394 // not a valid chain identifier
405 AlignSeq maxAlignseq = null;
406 String maxChainId = " ";
407 PDBChain maxChain = null;
408 boolean first = true;
409 for (int i = 0; i < pdb.chains.size(); i++)
411 PDBChain chain = (pdb.chains.elementAt(i));
412 if (targetChain.length() > 0 && !targetChain.equals(chain.id)
415 continue; // don't try to map chains don't match.
417 // TODO: correctly determine sequence type for mixed na/peptide
419 AlignSeq as = new AlignSeq(sequence[s],
420 pdb.chains.elementAt(i).sequence,
421 pdb.chains.elementAt(i).isNa ? AlignSeq.DNA
423 as.calcScoreMatrix();
426 if (first || as.maxscore > max
427 || (as.maxscore == max && chain.id.equals(targetChain)))
433 maxChainId = chain.id;
436 if (maxChain == null)
440 final StringBuffer mappingDetails = new StringBuffer();
441 mappingDetails.append("\n\nPDB Sequence is :\nSequence = "
442 + maxChain.sequence.getSequenceAsString());
443 mappingDetails.append("\nNo of residues = "
444 + maxChain.residues.size() + "\n\n");
445 PrintStream ps = new PrintStream(System.out)
448 public void print(String x)
450 mappingDetails.append(x);
454 public void println()
456 mappingDetails.append("\n");
460 maxAlignseq.printAlignment(ps);
462 mappingDetails.append("\nPDB start/end " + maxAlignseq.seq2start
463 + " " + maxAlignseq.seq2end);
464 mappingDetails.append("\nSEQ start/end "
465 + (maxAlignseq.seq1start + sequence[s].getStart() - 1) + " "
466 + (maxAlignseq.seq1end + sequence[s].getEnd() - 1));
468 maxChain.makeExactMapping(maxAlignseq, sequence[s]);
469 jalview.datamodel.Mapping sqmpping = maxAlignseq
470 .getMappingFromS1(false);
471 jalview.datamodel.Mapping omap = new jalview.datamodel.Mapping(
472 sqmpping.getMap().getInverse());
473 maxChain.transferRESNUMFeatures(sequence[s], null);
475 // allocate enough slots to store the mapping from positions in
476 // sequence[s] to the associated chain
477 int[][] mapping = new int[sequence[s].findPosition(sequence[s]
478 .getLength()) + 2][2];
484 Atom tmp = (Atom) maxChain.atoms.elementAt(index);
485 if (resNum != tmp.resNumber && tmp.alignmentMapping != -1)
487 resNum = tmp.resNumber;
488 mapping[tmp.alignmentMapping + 1][0] = tmp.resNumber;
489 mapping[tmp.alignmentMapping + 1][1] = tmp.atomIndex;
493 } while (index < maxChain.atoms.size());
495 if (protocol.equals(jalview.io.AppletFormatAdapter.PASTE))
497 pdbFile = "INLINE" + pdb.id;
499 StructureMapping newMapping = new StructureMapping(sequence[s],
500 pdbFile, pdb.id, maxChainId, mapping,
501 mappingDetails.toString());
502 if (forStructureView)
505 if (mappings == null)
507 mappings = new StructureMapping[1];
511 StructureMapping[] tmp = new StructureMapping[mappings.length + 1];
512 System.arraycopy(mappings, 0, tmp, 0, mappings.length);
516 mappings[mappings.length - 1] = newMapping;
518 maxChain.transferResidueAnnotation(newMapping, sqmpping);
525 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
527 listeners.removeElement(svl);
528 if (svl instanceof SequenceListener)
530 for (int i = 0; i < listeners.size(); i++)
532 if (listeners.elementAt(i) instanceof StructureListener)
534 ((StructureListener) listeners.elementAt(i))
535 .releaseReferences(svl);
540 if (pdbfiles == null)
545 Vector pdbs = new Vector();
546 for (int i = 0; i < pdbfiles.length; pdbs.addElement(pdbfiles[i++]))
550 StructureListener sl;
551 for (int i = 0; i < listeners.size(); i++)
553 if (listeners.elementAt(i) instanceof StructureListener)
555 sl = (StructureListener) listeners.elementAt(i);
556 handlepdbs = sl.getPdbFile();
557 for (int j = 0; j < handlepdbs.length; j++)
559 if (pdbs.contains(handlepdbs[j]))
561 pdbs.removeElement(handlepdbs[j]);
568 if (pdbs.size() > 0 && mappings != null)
570 Vector tmp = new Vector();
571 for (int i = 0; i < mappings.length; i++)
573 if (!pdbs.contains(mappings[i].pdbfile))
575 tmp.addElement(mappings[i]);
579 mappings = new StructureMapping[tmp.size()];
580 tmp.copyInto(mappings);
584 public void mouseOverStructure(int pdbResNum, String chain, String pdbfile)
586 if (listeners == null)
588 // old or prematurely sent event
591 boolean hasSequenceListeners = handlingVamsasMo || seqmappings != null;
592 SearchResults results = null;
593 SequenceI lastseq = null;
594 int lastipos = -1, indexpos;
595 for (int i = 0; i < listeners.size(); i++)
597 if (listeners.elementAt(i) instanceof SequenceListener)
601 results = new SearchResults();
603 if (mappings != null)
605 for (int j = 0; j < mappings.length; j++)
607 if (mappings[j].pdbfile.equals(pdbfile)
608 && mappings[j].pdbchain.equals(chain))
610 indexpos = mappings[j].getSeqPos(pdbResNum);
611 if (lastipos != indexpos && lastseq != mappings[j].sequence)
613 results.addResult(mappings[j].sequence, indexpos, indexpos);
615 lastseq = mappings[j].sequence;
616 // construct highlighted sequence list
617 if (seqmappings != null)
619 for (AlignedCodonFrame acf : seqmappings)
621 acf.markMappedRegion(mappings[j].sequence, indexpos,
633 for (int i = 0; i < listeners.size(); i++)
635 Object li = listeners.elementAt(i);
636 if (li instanceof SequenceListener)
638 ((SequenceListener) li).highlightSequence(results);
645 * highlight regions associated with a position (indexpos) in seq
648 * the sequence that the mouse over occurred on
650 * the absolute position being mouseovered in seq (0 to seq.length())
652 * the sequence position (if -1, seq.findPosition is called to
653 * resolve the residue number)
655 public void mouseOverSequence(SequenceI seq, int indexpos, int index,
658 boolean hasSequenceListeners = handlingVamsasMo || seqmappings != null;
659 SearchResults results = null;
662 index = seq.findPosition(indexpos);
664 for (int i = 0; i < listeners.size(); i++)
666 Object listener = listeners.elementAt(i);
667 if (listener == source)
669 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
670 // Temporary fudge with SequenceListener.getVamsasSource()
673 if (listener instanceof StructureListener)
675 highlightStructure((StructureListener) listener, seq, index);
679 if (listener instanceof SequenceListener)
681 final SequenceListener seqListener = (SequenceListener) listener;
682 if (hasSequenceListeners
683 && seqListener.getVamsasSource() != source)
685 if (relaySeqMappings)
689 results = buildSearchResults(seq, index);
691 seqListener.highlightSequence(results);
695 else if (listener instanceof VamsasListener && !handlingVamsasMo)
697 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
700 else if (listener instanceof SecondaryStructureListener)
702 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
710 * Returns a SearchResults object describing the mapped region corresponding
711 * to the specified sequence position.
717 protected SearchResults buildSearchResults(SequenceI seq, int index)
719 SearchResults results;
720 results = new SearchResults();
721 if (index >= seq.getStart() && index <= seq.getEnd())
723 if (seqmappings != null)
725 for (AlignedCodonFrame acf : seqmappings)
727 acf.markMappedRegion(seq, index, results);
730 // hasSequenceListeners = results.getSize() > 0;
731 if (handlingVamsasMo)
733 // maybe have to resolve seq to a dataset sequence...
734 // add in additional direct sequence and/or dataset sequence
736 results.addResult(seq, index, index);
743 * Send suitable messages to a StructureListener to highlight atoms
744 * corresponding to the given sequence position.
750 protected void highlightStructure(StructureListener sl, SequenceI seq,
754 if (mappings != null)
756 List<AtomSpec> atoms = new ArrayList<AtomSpec>();
757 for (int j = 0; j < mappings.length; j++)
759 if (mappings[j].sequence == seq
760 || mappings[j].sequence == seq.getDatasetSequence())
762 atomNo = mappings[j].getAtomNum(index);
766 atoms.add(new AtomSpec(mappings[j].pdbfile,
767 mappings[j].pdbchain, mappings[j].getPDBResNum(index),
772 sl.highlightAtoms(atoms);
777 * true if a mouse over event from an external (ie Vamsas) source is being
780 boolean handlingVamsasMo = false;
785 * as mouseOverSequence but only route event to SequenceListeners
789 * in an alignment sequence
791 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
794 handlingVamsasMo = true;
795 long msg = sequenceI.hashCode() * (1 + position);
799 mouseOverSequence(sequenceI, position, -1, source);
801 handlingVamsasMo = false;
804 public Annotation[] colourSequenceFromStructure(SequenceI seq,
808 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
809 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
811 * Annotation [] annotations = new Annotation[seq.getLength()];
813 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
814 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
815 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
817 * for (int j = 0; j < mappings.length; j++) {
819 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
820 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
821 * System.out.println(pdbid+" "+mappings[j].getPdbId() +"
822 * "+mappings[j].pdbfile);
824 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
825 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
827 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
828 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
829 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
830 * mappings[j].pdbfile); }
832 * annotations[index] = new Annotation("X",null,' ',0,col); } return
833 * annotations; } } } }
835 * return annotations;
839 public void structureSelectionChanged()
843 public void sequenceSelectionChanged()
847 public void sequenceColoursChanged(Object source)
849 StructureListener sl;
850 for (int i = 0; i < listeners.size(); i++)
852 if (listeners.elementAt(i) instanceof StructureListener)
854 sl = (StructureListener) listeners.elementAt(i);
855 sl.updateColours(source);
860 public StructureMapping[] getMapping(String pdbfile)
862 Vector tmp = new Vector();
863 if (mappings != null)
865 for (int i = 0; i < mappings.length; i++)
867 if (mappings[i].pdbfile.equals(pdbfile))
869 tmp.addElement(mappings[i]);
873 StructureMapping[] ret = new StructureMapping[tmp.size()];
874 for (int i = 0; i < tmp.size(); i++)
876 ret[i] = (StructureMapping) tmp.elementAt(i);
882 public String printMapping(String pdbfile)
884 StringBuffer sb = new StringBuffer();
885 for (int i = 0; i < mappings.length; i++)
887 if (mappings[i].pdbfile.equals(pdbfile))
889 sb.append(mappings[i].mappingDetails);
893 return sb.toString();
897 * Remove each of the given codonFrames from the stored set (if present).
901 public void removeMappings(AlignedCodonFrame[] codonFrames)
903 if (codonFrames != null)
905 for (AlignedCodonFrame acf : codonFrames)
907 seqmappings.remove(acf);
913 * Add each of the given codonFrames to the stored set (if not aready
918 public void addMappings(AlignedCodonFrame[] codonFrames)
920 if (codonFrames != null)
922 for (AlignedCodonFrame acf : codonFrames)
924 seqmappings.add(acf);
929 Vector<SelectionListener> sel_listeners = new Vector<SelectionListener>();
931 public void addSelectionListener(SelectionListener selecter)
933 if (!sel_listeners.contains(selecter))
935 sel_listeners.addElement(selecter);
939 public void removeSelectionListener(SelectionListener toremove)
941 if (sel_listeners.contains(toremove))
943 sel_listeners.removeElement(toremove);
947 public synchronized void sendSelection(
948 jalview.datamodel.SequenceGroup selection,
949 jalview.datamodel.ColumnSelection colsel, SelectionSource source)
951 if (sel_listeners != null && sel_listeners.size() > 0)
953 Enumeration listeners = sel_listeners.elements();
954 while (listeners.hasMoreElements())
956 SelectionListener slis = ((SelectionListener) listeners
960 slis.selection(selection, colsel, source);
967 Vector<AlignmentViewPanelListener> view_listeners = new Vector<AlignmentViewPanelListener>();
969 public synchronized void sendViewPosition(
970 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
971 int startSeq, int endSeq)
974 if (view_listeners != null && view_listeners.size() > 0)
976 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
978 while (listeners.hasMoreElements())
980 AlignmentViewPanelListener slis = listeners.nextElement();
983 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
991 * release all references associated with this manager provider
995 public static void release(StructureSelectionManagerProvider jalviewLite)
997 // synchronized (instances)
999 if (instances == null)
1003 StructureSelectionManager mnger = (instances.get(jalviewLite));
1006 instances.remove(jalviewLite);
1010 } catch (Throwable x)
1017 public void registerPDBEntry(PDBEntry pdbentry)
1019 if (pdbentry.getFile() != null
1020 && pdbentry.getFile().trim().length() > 0)
1022 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1026 public void addCommandListener(CommandListener cl)
1028 if (!commandListeners.contains(cl))
1030 commandListeners.add(cl);
1034 public boolean hasCommandListener(CommandListener cl)
1036 return this.commandListeners.contains(cl);
1039 public boolean removeEditListener(CommandListener l)
1041 return commandListeners.remove(l);
1045 * Forward a command to any command listeners (except for the command's
1049 * the command to be broadcast (in its form after being performed)
1051 * if true, the command was being 'undone'
1054 public void commandPerformed(CommandI command, boolean undo,
1055 VamsasSource source)
1057 for (CommandListener listener : commandListeners)
1059 if (listener.getVamsasSource() != source)
1061 listener.mirrorCommand(command, undo, this);
1067 * Returns a new EditCommand representing the given command as mapped to the
1068 * given sequences. If there is no mapping, returns an empty EditCommand.
1076 public EditCommand mapEditCommand(EditCommand command, boolean undo,
1077 final List<SequenceI> targetSeqs, char gapChar)
1080 * Cache a copy of the target sequences so we can mimic successive edits on
1081 * them. This lets us compute mappings for all edits in the set.
1083 Map<SequenceI, SequenceI> targetCopies = new HashMap<SequenceI, SequenceI>();
1084 for (SequenceI seq : targetSeqs)
1086 SequenceI ds = seq.getDatasetSequence();
1089 final Sequence copy = new Sequence("", new String(seq.getSequence()));
1090 copy.setDatasetSequence(ds);
1091 targetCopies.put(ds, copy);
1096 * Compute 'source' sequences as they were before applying edits:
1098 Map<SequenceI, SequenceI> originalSequences = command.priorState(undo);
1100 EditCommand result = new EditCommand();
1101 Iterator<Edit> edits = command.getEditIterator(!undo);
1102 while (edits.hasNext())
1104 Edit edit = edits.next();
1105 Action action = edit.getAction();
1108 * Invert sense of action if an Undo.
1112 action = action == Action.INSERT_GAP ? Action.DELETE_GAP
1113 : (action == Action.DELETE_GAP ? Action.INSERT_GAP : action);
1115 final int count = edit.getNumber();
1116 final int editPos = edit.getPosition();
1117 for (SequenceI seq : edit.getSequences())
1120 * Get residue position at (or to right of) edit location. Note we use
1121 * our 'copy' of the sequence before editing for this.
1123 SequenceI ds = seq.getDatasetSequence();
1128 final SequenceI actedOn = originalSequences.get(ds);
1129 final int seqpos = actedOn.findPosition(editPos);
1132 * Determine all mappings from this position to mapped sequences.
1134 SearchResults sr = buildSearchResults(seq, seqpos);
1138 for (SequenceI targetSeq : targetSeqs)
1140 ds = targetSeq.getDatasetSequence();
1145 SequenceI copyTarget = targetCopies.get(ds);
1146 final int[] match = sr.getResults(copyTarget, 0,
1147 copyTarget.getLength());
1150 final int ratio = 3; // TODO: compute this - how?
1151 final int mappedCount = count * ratio;
1154 * Shift Delete start position left, as it acts on positions to
1157 int mappedEditPos = action == Action.DELETE_GAP ? match[0]
1158 - mappedCount : match[0];
1159 Edit e = new EditCommand().new Edit(action, new SequenceI[]
1160 { targetSeq }, mappedEditPos, mappedCount, gapChar);
1164 * and 'apply' the edit to our copy of its target sequence
1166 if (action == Action.INSERT_GAP)
1168 copyTarget.setSequence(new String(StringUtils.insertCharAt(
1169 copyTarget.getSequence(), mappedEditPos,
1173 else if (action == Action.DELETE_GAP)
1175 copyTarget.setSequence(new String(StringUtils.deleteChars(
1176 copyTarget.getSequence(), mappedEditPos,
1177 mappedEditPos + mappedCount)));
1183 * and 'apply' the edit to our copy of its source sequence
1185 if (action == Action.INSERT_GAP) {
1186 actedOn.setSequence(new String(StringUtils.insertCharAt(
1187 actedOn.getSequence(), editPos, count, gapChar)));
1189 else if (action == Action.DELETE_GAP)
1191 actedOn.setSequence(new String(StringUtils.deleteChars(
1192 actedOn.getSequence(), editPos, editPos + count)));