2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ 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.AlignmentViewPanel;
25 import jalview.api.StructureSelectionManagerProvider;
26 import jalview.commands.CommandI;
27 import jalview.commands.EditCommand;
28 import jalview.commands.OrderCommand;
29 import jalview.datamodel.AlignedCodonFrame;
30 import jalview.datamodel.AlignmentAnnotation;
31 import jalview.datamodel.AlignmentI;
32 import jalview.datamodel.Annotation;
33 import jalview.datamodel.ColumnSelection;
34 import jalview.datamodel.Mapping;
35 import jalview.datamodel.PDBEntry;
36 import jalview.datamodel.SearchResults;
37 import jalview.datamodel.SequenceGroup;
38 import jalview.datamodel.SequenceI;
39 import jalview.io.AppletFormatAdapter;
40 import jalview.util.MappingUtils;
41 import jalview.util.MessageManager;
43 import java.io.PrintStream;
44 import java.util.ArrayList;
45 import java.util.Arrays;
46 import java.util.Collections;
47 import java.util.Enumeration;
48 import java.util.HashMap;
49 import java.util.IdentityHashMap;
50 import java.util.LinkedHashSet;
51 import java.util.List;
54 import java.util.Vector;
57 import MCview.PDBChain;
58 import MCview.PDBfile;
60 public class StructureSelectionManager
62 public final static String NEWLINE = System.lineSeparator();
64 static IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> instances;
66 private List<StructureMapping> mappings = new ArrayList<StructureMapping>();
68 private boolean processSecondaryStructure = false;
70 private boolean secStructServices = false;
72 private boolean addTempFacAnnot = false;
75 * Set of any registered mappings between (dataset) sequences.
77 Set<AlignedCodonFrame> seqmappings = new LinkedHashSet<AlignedCodonFrame>();
80 * Reference counters for the above mappings. Remove mappings when ref count
83 Map<AlignedCodonFrame, Integer> seqMappingRefCounts = new HashMap<AlignedCodonFrame, Integer>();
85 private List<CommandListener> commandListeners = new ArrayList<CommandListener>();
87 private List<SelectionListener> sel_listeners = new ArrayList<SelectionListener>();
90 * @return true if will try to use external services for processing secondary
93 public boolean isSecStructServices()
95 return secStructServices;
99 * control use of external services for processing secondary structure
101 * @param secStructServices
103 public void setSecStructServices(boolean secStructServices)
105 this.secStructServices = secStructServices;
109 * flag controlling addition of any kind of structural annotation
111 * @return true if temperature factor annotation will be added
113 public boolean isAddTempFacAnnot()
115 return addTempFacAnnot;
119 * set flag controlling addition of structural annotation
121 * @param addTempFacAnnot
123 public void setAddTempFacAnnot(boolean addTempFacAnnot)
125 this.addTempFacAnnot = addTempFacAnnot;
130 * @return if true, the structure manager will attempt to add secondary
131 * structure lines for unannotated sequences
134 public boolean isProcessSecondaryStructure()
136 return processSecondaryStructure;
140 * Control whether structure manager will try to annotate mapped sequences
141 * with secondary structure from PDB data.
145 public void setProcessSecondaryStructure(boolean enable)
147 processSecondaryStructure = enable;
151 * debug function - write all mappings to stdout
153 public void reportMapping()
155 if (mappings.isEmpty())
157 System.err.println("reportMapping: No PDB/Sequence mappings.");
161 System.err.println("reportMapping: There are " + mappings.size()
164 for (StructureMapping sm : mappings)
166 System.err.println("mapping " + i++ + " : " + sm.pdbfile);
172 * map between the PDB IDs (or structure identifiers) used by Jalview and the
173 * absolute filenames for PDB data that corresponds to it
175 Map<String, String> pdbIdFileName = new HashMap<String, String>();
177 Map<String, String> pdbFileNameId = new HashMap<String, String>();
179 public void registerPDBFile(String idForFile, String absoluteFile)
181 pdbIdFileName.put(idForFile, absoluteFile);
182 pdbFileNameId.put(absoluteFile, idForFile);
185 public String findIdForPDBFile(String idOrFile)
187 String id = pdbFileNameId.get(idOrFile);
191 public String findFileForPDBId(String idOrFile)
193 String id = pdbIdFileName.get(idOrFile);
197 public boolean isPDBFileRegistered(String idOrFile)
199 return pdbFileNameId.containsKey(idOrFile)
200 || pdbIdFileName.containsKey(idOrFile);
203 private static StructureSelectionManager nullProvider = null;
205 public static StructureSelectionManager getStructureSelectionManager(
206 StructureSelectionManagerProvider context)
210 if (nullProvider == null)
212 if (instances != null)
216 .getString("error.implementation_error_structure_selection_manager_null"),
217 new NullPointerException(MessageManager
218 .getString("exception.ssm_context_is_null")));
222 nullProvider = new StructureSelectionManager();
227 if (instances == null)
229 instances = new java.util.IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager>();
231 StructureSelectionManager instance = instances.get(context);
232 if (instance == null)
234 if (nullProvider != null)
236 instance = nullProvider;
240 instance = new StructureSelectionManager();
242 instances.put(context, instance);
248 * flag controlling whether SeqMappings are relayed from received sequence
249 * mouse over events to other sequences
251 boolean relaySeqMappings = true;
254 * Enable or disable relay of seqMapping events to other sequences. You might
255 * want to do this if there are many sequence mappings and the host computer
260 public void setRelaySeqMappings(boolean relay)
262 relaySeqMappings = relay;
266 * get the state of the relay seqMappings flag.
268 * @return true if sequence mouse overs are being relayed to other mapped
271 public boolean isRelaySeqMappingsEnabled()
273 return relaySeqMappings;
276 Vector listeners = new Vector();
279 * register a listener for alignment sequence mouseover events
283 public void addStructureViewerListener(Object svl)
285 if (!listeners.contains(svl))
287 listeners.addElement(svl);
292 * Returns the file name for a mapped PDB id (or null if not mapped).
297 public String alreadyMappedToFile(String pdbid)
299 for (StructureMapping sm : mappings)
301 if (sm.getPdbId().equals(pdbid))
310 * Import structure data and register a structure mapping for broadcasting
311 * colouring, mouseovers and selection events (convenience wrapper).
314 * - one or more sequences to be mapped to pdbFile
315 * @param targetChains
316 * - optional chain specification for mapping each sequence to pdb
317 * (may be nill, individual elements may be nill)
319 * - structure data resource
321 * - how to resolve data from resource
322 * @return null or the structure data parsed as a pdb file
324 synchronized public PDBfile setMapping(SequenceI[] sequence,
325 String[] targetChains, String pdbFile, String protocol)
327 return setMapping(true, sequence, targetChains, pdbFile, protocol);
331 * create sequence structure mappings between each sequence and the given
332 * pdbFile (retrieved via the given protocol).
334 * @param forStructureView
335 * when true, record the mapping for use in mouseOvers
338 * - one or more sequences to be mapped to pdbFile
339 * @param targetChains
340 * - optional chain specification for mapping each sequence to pdb
341 * (may be nill, individual elements may be nill)
343 * - structure data resource
345 * - how to resolve data from resource
346 * @return null or the structure data parsed as a pdb file
348 synchronized public PDBfile setMapping(boolean forStructureView,
349 SequenceI[] sequence, String[] targetChains, String pdbFile,
353 * There will be better ways of doing this in the future, for now we'll use
354 * the tried and tested MCview pdb mapping
356 boolean parseSecStr = processSecondaryStructure;
357 if (isPDBFileRegistered(pdbFile))
359 for (SequenceI sq : sequence)
362 while (ds.getDatasetSequence() != null)
364 ds = ds.getDatasetSequence();
367 if (ds.getAnnotation() != null)
369 for (AlignmentAnnotation ala : ds.getAnnotation())
371 // false if any annotation present from this structure
372 // JBPNote this fails for jmol/chimera view because the *file* is
373 // passed, not the structure data ID -
374 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
385 pdb = new PDBfile(addTempFacAnnot, parseSecStr, secStructServices,
387 if (pdb.id != null && pdb.id.trim().length() > 0
388 && AppletFormatAdapter.FILE.equals(protocol))
390 registerPDBFile(pdb.id.trim(), pdbFile);
392 } catch (Exception ex)
394 ex.printStackTrace();
399 for (int s = 0; s < sequence.length; s++)
401 boolean infChain = true;
402 final SequenceI seq = sequence[s];
403 if (targetChains != null && targetChains[s] != null)
406 targetChain = targetChains[s];
408 else if (seq.getName().indexOf("|") > -1)
410 targetChain = seq.getName().substring(
411 seq.getName().lastIndexOf("|") + 1);
412 if (targetChain.length() > 1)
414 if (targetChain.trim().length() == 0)
420 // not a valid chain identifier
431 * Attempt pairwise alignment of the sequence with each chain in the PDB,
432 * and remember the highest scoring chain
435 AlignSeq maxAlignseq = null;
436 String maxChainId = " ";
437 PDBChain maxChain = null;
438 boolean first = true;
439 for (PDBChain chain : pdb.chains)
441 if (targetChain.length() > 0 && !targetChain.equals(chain.id)
444 continue; // don't try to map chains don't match.
446 // TODO: correctly determine sequence type for mixed na/peptide
448 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
449 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
452 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
453 // as.calcScoreMatrix();
454 // as.traceAlignment();
456 if (first || as.maxscore > max
457 || (as.maxscore == max && chain.id.equals(targetChain)))
463 maxChainId = chain.id;
466 if (maxChain == null)
470 final StringBuilder mappingDetails = new StringBuilder(128);
471 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
472 .append(NEWLINE).append("Sequence = ")
473 .append(maxChain.sequence.getSequenceAsString());
474 mappingDetails.append(NEWLINE).append("No of residues = ")
475 .append(maxChain.residues.size()).append(NEWLINE)
477 PrintStream ps = new PrintStream(System.out)
480 public void print(String x)
482 mappingDetails.append(x);
486 public void println()
488 mappingDetails.append(NEWLINE);
492 maxAlignseq.printAlignment(ps);
494 mappingDetails.append(NEWLINE).append("PDB start/end ");
495 mappingDetails.append(String.valueOf(maxAlignseq.seq2start)).append(
497 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
499 mappingDetails.append(NEWLINE).append("SEQ start/end ");
500 mappingDetails.append(
501 String.valueOf(maxAlignseq.seq1start + seq.getStart() - 1))
503 mappingDetails.append(String.valueOf(maxAlignseq.seq1end
504 + seq.getEnd() - 1));
506 maxChain.makeExactMapping(maxAlignseq, seq);
507 Mapping sqmpping = maxAlignseq.getMappingFromS1(false);
508 Mapping omap = new Mapping(sqmpping.getMap().getInverse());
509 maxChain.transferRESNUMFeatures(seq, null);
511 // allocate enough slots to store the mapping from positions in
512 // sequence[s] to the associated chain
513 int[][] mapping = new int[seq.findPosition(seq.getLength()) + 2][2];
519 Atom tmp = maxChain.atoms.elementAt(index);
520 if (resNum != tmp.resNumber && tmp.alignmentMapping != -1)
522 resNum = tmp.resNumber;
523 mapping[tmp.alignmentMapping + 1][0] = tmp.resNumber;
524 mapping[tmp.alignmentMapping + 1][1] = tmp.atomIndex;
528 } while (index < maxChain.atoms.size());
530 if (protocol.equals(AppletFormatAdapter.PASTE))
532 pdbFile = "INLINE" + pdb.id;
534 StructureMapping newMapping = new StructureMapping(seq, pdbFile,
535 pdb.id, maxChainId, mapping, mappingDetails.toString());
536 if (forStructureView)
538 mappings.add(newMapping);
540 maxChain.transferResidueAnnotation(newMapping, sqmpping);
547 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
549 listeners.removeElement(svl);
550 if (svl instanceof SequenceListener)
552 for (int i = 0; i < listeners.size(); i++)
554 if (listeners.elementAt(i) instanceof StructureListener)
556 ((StructureListener) listeners.elementAt(i))
557 .releaseReferences(svl);
562 if (pdbfiles == null)
568 * Remove mappings to the closed listener's PDB files, but first check if
569 * another listener is still interested
571 List<String> pdbs = new ArrayList<String>(Arrays.asList(pdbfiles));
573 StructureListener sl;
574 for (int i = 0; i < listeners.size(); i++)
576 if (listeners.elementAt(i) instanceof StructureListener)
578 sl = (StructureListener) listeners.elementAt(i);
579 for (String pdbfile : sl.getPdbFile())
581 pdbs.remove(pdbfile);
587 * Rebuild the mappings set, retaining only those which are for 'other' PDB
592 List<StructureMapping> tmp = new ArrayList<StructureMapping>();
593 for (StructureMapping sm : mappings)
595 if (!pdbs.contains(sm.pdbfile))
606 * Propagate mouseover of a single position in a structure
612 public void mouseOverStructure(int pdbResNum, String chain, String pdbfile)
614 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
615 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
616 mouseOverStructure(atoms);
620 * Propagate mouseover or selection of multiple positions in a structure
624 public void mouseOverStructure(List<AtomSpec> atoms)
626 if (listeners == null)
628 // old or prematurely sent event
631 boolean hasSequenceListener = false;
632 for (int i = 0; i < listeners.size(); i++)
634 if (listeners.elementAt(i) instanceof SequenceListener)
636 hasSequenceListener = true;
639 if (!hasSequenceListener)
644 SearchResults results = new SearchResults();
645 for (AtomSpec atom : atoms)
647 SequenceI lastseq = null;
649 for (StructureMapping sm : mappings)
651 if (sm.pdbfile.equals(atom.getPdbFile())
652 && sm.pdbchain.equals(atom.getChain()))
654 int indexpos = sm.getSeqPos(atom.getPdbResNum());
655 if (lastipos != indexpos && lastseq != sm.sequence)
657 results.addResult(sm.sequence, indexpos, indexpos);
659 lastseq = sm.sequence;
660 // construct highlighted sequence list
661 for (AlignedCodonFrame acf : seqmappings)
663 acf.markMappedRegion(sm.sequence, indexpos, results);
669 for (Object li : listeners)
671 if (li instanceof SequenceListener)
673 ((SequenceListener) li).highlightSequence(results);
679 * highlight regions associated with a position (indexpos) in seq
682 * the sequence that the mouse over occurred on
684 * the absolute position being mouseovered in seq (0 to seq.length())
686 * the sequence position (if -1, seq.findPosition is called to
687 * resolve the residue number)
689 public void mouseOverSequence(SequenceI seq, int indexpos, int index,
692 boolean hasSequenceListeners = handlingVamsasMo
693 || !seqmappings.isEmpty();
694 SearchResults results = null;
697 index = seq.findPosition(indexpos);
699 for (int i = 0; i < listeners.size(); i++)
701 Object listener = listeners.elementAt(i);
702 if (listener == source)
704 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
705 // Temporary fudge with SequenceListener.getVamsasSource()
708 if (listener instanceof StructureListener)
710 highlightStructure((StructureListener) listener, seq, index);
714 if (listener instanceof SequenceListener)
716 final SequenceListener seqListener = (SequenceListener) listener;
717 if (hasSequenceListeners
718 && seqListener.getVamsasSource() != source)
720 if (relaySeqMappings)
724 results = MappingUtils.buildSearchResults(seq, index,
727 if (handlingVamsasMo)
729 results.addResult(seq, index, index);
732 seqListener.highlightSequence(results);
736 else if (listener instanceof VamsasListener && !handlingVamsasMo)
738 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
741 else if (listener instanceof SecondaryStructureListener)
743 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
751 * Send suitable messages to a StructureListener to highlight atoms
752 * corresponding to the given sequence position.
758 protected void highlightStructure(StructureListener sl, SequenceI seq,
761 if (!sl.isListeningFor(seq))
766 List<AtomSpec> atoms = new ArrayList<AtomSpec>();
767 for (StructureMapping sm : mappings)
769 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence())
771 atomNo = sm.getAtomNum(index);
775 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain, sm
776 .getPDBResNum(index), atomNo));
780 sl.highlightAtoms(atoms);
784 * true if a mouse over event from an external (ie Vamsas) source is being
787 boolean handlingVamsasMo = false;
792 * as mouseOverSequence but only route event to SequenceListeners
796 * in an alignment sequence
798 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
801 handlingVamsasMo = true;
802 long msg = sequenceI.hashCode() * (1 + position);
806 mouseOverSequence(sequenceI, position, -1, source);
808 handlingVamsasMo = false;
811 public Annotation[] colourSequenceFromStructure(SequenceI seq,
815 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
816 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
818 * Annotation [] annotations = new Annotation[seq.getLength()];
820 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
821 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
822 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
824 * for (int j = 0; j < mappings.length; j++) {
826 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
827 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
828 * System.out.println(pdbid+" "+mappings[j].getPdbId() +"
829 * "+mappings[j].pdbfile);
831 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
832 * if(Comparison.isGap(seq.getCharAt(index))) continue;
834 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
835 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
836 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
837 * mappings[j].pdbfile); }
839 * annotations[index] = new Annotation("X",null,' ',0,col); } return
840 * annotations; } } } }
842 * return annotations;
846 public void structureSelectionChanged()
850 public void sequenceSelectionChanged()
854 public void sequenceColoursChanged(Object source)
856 StructureListener sl;
857 for (int i = 0; i < listeners.size(); i++)
859 if (listeners.elementAt(i) instanceof StructureListener)
861 sl = (StructureListener) listeners.elementAt(i);
862 sl.updateColours(source);
867 public StructureMapping[] getMapping(String pdbfile)
869 List<StructureMapping> tmp = new ArrayList<StructureMapping>();
870 for (StructureMapping sm : mappings)
872 if (sm.pdbfile.equals(pdbfile))
877 return tmp.toArray(new StructureMapping[tmp.size()]);
881 * Returns a readable description of all mappings for the given pdbfile to any
882 * of the given sequences
888 public String printMappings(String pdbfile, List<SequenceI> seqs)
890 if (pdbfile == null || seqs == null || seqs.isEmpty())
895 StringBuilder sb = new StringBuilder(64);
896 for (StructureMapping sm : mappings)
898 if (sm.pdbfile.equals(pdbfile) && seqs.contains(sm.sequence))
900 sb.append(sm.mappingDetails);
902 // separator makes it easier to read multiple mappings
903 sb.append("=====================");
909 return sb.toString();
913 * Decrement the reference counter for each of the given mappings, and remove
914 * it entirely if its reference counter reduces to zero.
918 public void removeMappings(Set<AlignedCodonFrame> set)
922 for (AlignedCodonFrame acf : set)
930 * Decrement the reference counter for the given mapping, and remove it
931 * entirely if its reference counter reduces to zero.
935 public void removeMapping(AlignedCodonFrame acf)
937 if (acf != null && seqmappings.contains(acf))
939 int count = seqMappingRefCounts.get(acf);
943 seqMappingRefCounts.put(acf, count);
947 seqmappings.remove(acf);
948 seqMappingRefCounts.remove(acf);
954 * Add each of the given codonFrames to the stored set. If not aready present,
955 * increments its reference count instead.
959 public void addMappings(Set<AlignedCodonFrame> set)
963 for (AlignedCodonFrame acf : set)
971 * Add the given mapping to the stored set, or if already stored, increment
972 * its reference counter.
974 public void addMapping(AlignedCodonFrame acf)
978 if (seqmappings.contains(acf))
980 seqMappingRefCounts.put(acf, seqMappingRefCounts.get(acf) + 1);
984 seqmappings.add(acf);
985 seqMappingRefCounts.put(acf, 1);
990 public void addSelectionListener(SelectionListener selecter)
992 if (!sel_listeners.contains(selecter))
994 sel_listeners.add(selecter);
998 public void removeSelectionListener(SelectionListener toremove)
1000 if (sel_listeners.contains(toremove))
1002 sel_listeners.remove(toremove);
1006 public synchronized void sendSelection(SequenceGroup selection,
1007 ColumnSelection colsel, SelectionSource source)
1009 for (SelectionListener slis : sel_listeners)
1013 slis.selection(selection, colsel, source);
1018 Vector<AlignmentViewPanelListener> view_listeners = new Vector<AlignmentViewPanelListener>();
1020 public synchronized void sendViewPosition(
1021 AlignmentViewPanel source,
1022 int startRes, int endRes,
1023 int startSeq, int endSeq)
1026 if (view_listeners != null && view_listeners.size() > 0)
1028 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1030 while (listeners.hasMoreElements())
1032 AlignmentViewPanelListener slis = listeners.nextElement();
1035 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1043 * release all references associated with this manager provider
1045 * @param jalviewLite
1047 public static void release(StructureSelectionManagerProvider jalviewLite)
1049 // synchronized (instances)
1051 if (instances == null)
1055 StructureSelectionManager mnger = (instances.get(jalviewLite));
1058 instances.remove(jalviewLite);
1062 } catch (Throwable x)
1069 public void registerPDBEntry(PDBEntry pdbentry)
1071 if (pdbentry.getFile() != null
1072 && pdbentry.getFile().trim().length() > 0)
1074 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1078 public void addCommandListener(CommandListener cl)
1080 if (!commandListeners.contains(cl))
1082 commandListeners.add(cl);
1086 public boolean hasCommandListener(CommandListener cl)
1088 return this.commandListeners.contains(cl);
1091 public boolean removeCommandListener(CommandListener l)
1093 return commandListeners.remove(l);
1097 * Forward a command to any command listeners (except for the command's
1101 * the command to be broadcast (in its form after being performed)
1103 * if true, the command was being 'undone'
1106 public void commandPerformed(CommandI command, boolean undo,
1107 VamsasSource source)
1109 for (CommandListener listener : commandListeners)
1111 listener.mirrorCommand(command, undo, this, source);
1116 * Returns a new CommandI representing the given command as mapped to the
1117 * given sequences. If no mapping could be made, or the command is not of a
1118 * mappable kind, returns null.
1126 public CommandI mapCommand(CommandI command, boolean undo,
1127 final AlignmentI mapTo, char gapChar)
1129 if (command instanceof EditCommand)
1131 return MappingUtils.mapEditCommand((EditCommand) command, undo,
1132 mapTo, gapChar, seqmappings);
1134 else if (command instanceof OrderCommand)
1136 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1137 mapTo, seqmappings);