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.StructureSelectionManagerProvider;
25 import jalview.commands.CommandI;
26 import jalview.commands.EditCommand;
27 import jalview.commands.OrderCommand;
28 import jalview.datamodel.AlignedCodonFrame;
29 import jalview.datamodel.AlignmentAnnotation;
30 import jalview.datamodel.AlignmentI;
31 import jalview.datamodel.Annotation;
32 import jalview.datamodel.PDBEntry;
33 import jalview.datamodel.SearchResults;
34 import jalview.datamodel.SequenceI;
35 import jalview.io.AppletFormatAdapter;
36 import jalview.util.MappingUtils;
37 import jalview.util.MessageManager;
39 import java.io.PrintStream;
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.Collections;
43 import java.util.Enumeration;
44 import java.util.HashMap;
45 import java.util.IdentityHashMap;
46 import java.util.LinkedHashSet;
47 import java.util.List;
50 import java.util.Vector;
53 import MCview.PDBChain;
54 import MCview.PDBfile;
56 public class StructureSelectionManager
58 public final static String NEWLINE = System.lineSeparator();
60 static IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> instances;
62 private List<StructureMapping> mappings = new ArrayList<StructureMapping>();
64 private boolean processSecondaryStructure = false;
66 private boolean secStructServices = false;
68 private boolean addTempFacAnnot = false;
71 * Set of any registered mappings between (dataset) sequences.
73 public Set<AlignedCodonFrame> seqmappings = new LinkedHashSet<AlignedCodonFrame>();
75 private List<CommandListener> commandListeners = new ArrayList<CommandListener>();
77 private List<SelectionListener> sel_listeners = new ArrayList<SelectionListener>();
80 * @return true if will try to use external services for processing secondary
83 public boolean isSecStructServices()
85 return secStructServices;
89 * control use of external services for processing secondary structure
91 * @param secStructServices
93 public void setSecStructServices(boolean secStructServices)
95 this.secStructServices = secStructServices;
99 * flag controlling addition of any kind of structural annotation
101 * @return true if temperature factor annotation will be added
103 public boolean isAddTempFacAnnot()
105 return addTempFacAnnot;
109 * set flag controlling addition of structural annotation
111 * @param addTempFacAnnot
113 public void setAddTempFacAnnot(boolean addTempFacAnnot)
115 this.addTempFacAnnot = addTempFacAnnot;
120 * @return if true, the structure manager will attempt to add secondary
121 * structure lines for unannotated sequences
124 public boolean isProcessSecondaryStructure()
126 return processSecondaryStructure;
130 * Control whether structure manager will try to annotate mapped sequences
131 * with secondary structure from PDB data.
135 public void setProcessSecondaryStructure(boolean enable)
137 processSecondaryStructure = enable;
141 * debug function - write all mappings to stdout
143 public void reportMapping()
145 if (mappings.isEmpty())
147 System.err.println("reportMapping: No PDB/Sequence mappings.");
151 System.err.println("reportMapping: There are " + mappings.size()
154 for (StructureMapping sm : mappings)
156 System.err.println("mapping " + i++ + " : " + sm.pdbfile);
162 * map between the PDB IDs (or structure identifiers) used by Jalview and the
163 * absolute filenames for PDB data that corresponds to it
165 Map<String, String> pdbIdFileName = new HashMap<String, String>();
167 Map<String, String> pdbFileNameId = new HashMap<String, String>();
169 public void registerPDBFile(String idForFile, String absoluteFile)
171 pdbIdFileName.put(idForFile, absoluteFile);
172 pdbFileNameId.put(absoluteFile, idForFile);
175 public String findIdForPDBFile(String idOrFile)
177 String id = pdbFileNameId.get(idOrFile);
181 public String findFileForPDBId(String idOrFile)
183 String id = pdbIdFileName.get(idOrFile);
187 public boolean isPDBFileRegistered(String idOrFile)
189 return pdbFileNameId.containsKey(idOrFile)
190 || pdbIdFileName.containsKey(idOrFile);
193 private static StructureSelectionManager nullProvider = null;
195 public static StructureSelectionManager getStructureSelectionManager(
196 StructureSelectionManagerProvider context)
200 if (nullProvider == null)
202 if (instances != null)
206 .getString("error.implementation_error_structure_selection_manager_null"),
207 new NullPointerException(MessageManager
208 .getString("exception.ssm_context_is_null")));
212 nullProvider = new StructureSelectionManager();
217 if (instances == null)
219 instances = new java.util.IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager>();
221 StructureSelectionManager instance = instances.get(context);
222 if (instance == null)
224 if (nullProvider != null)
226 instance = nullProvider;
230 instance = new StructureSelectionManager();
232 instances.put(context, instance);
238 * flag controlling whether SeqMappings are relayed from received sequence
239 * mouse over events to other sequences
241 boolean relaySeqMappings = true;
244 * Enable or disable relay of seqMapping events to other sequences. You might
245 * want to do this if there are many sequence mappings and the host computer
250 public void setRelaySeqMappings(boolean relay)
252 relaySeqMappings = relay;
256 * get the state of the relay seqMappings flag.
258 * @return true if sequence mouse overs are being relayed to other mapped
261 public boolean isRelaySeqMappingsEnabled()
263 return relaySeqMappings;
266 Vector listeners = new Vector();
269 * register a listener for alignment sequence mouseover events
273 public void addStructureViewerListener(Object svl)
275 if (!listeners.contains(svl))
277 listeners.addElement(svl);
282 * Returns the file name for a mapped PDB id (or null if not mapped).
287 public String alreadyMappedToFile(String pdbid)
289 for (StructureMapping sm : mappings)
291 if (sm.getPdbId().equals(pdbid))
300 * Import structure data and register a structure mapping for broadcasting
301 * colouring, mouseovers and selection events (convenience wrapper).
304 * - one or more sequences to be mapped to pdbFile
305 * @param targetChains
306 * - optional chain specification for mapping each sequence to pdb
307 * (may be nill, individual elements may be nill)
309 * - structure data resource
311 * - how to resolve data from resource
312 * @return null or the structure data parsed as a pdb file
314 synchronized public PDBfile setMapping(SequenceI[] sequence,
315 String[] targetChains, String pdbFile, String protocol)
317 return setMapping(true, sequence, targetChains, pdbFile, protocol);
321 * create sequence structure mappings between each sequence and the given
322 * pdbFile (retrieved via the given protocol).
324 * @param forStructureView
325 * when true, record the mapping for use in mouseOvers
328 * - one or more sequences to be mapped to pdbFile
329 * @param targetChains
330 * - optional chain specification for mapping each sequence to pdb
331 * (may be nill, individual elements may be nill)
333 * - structure data resource
335 * - how to resolve data from resource
336 * @return null or the structure data parsed as a pdb file
338 synchronized public PDBfile setMapping(boolean forStructureView,
339 SequenceI[] sequence, String[] targetChains, String pdbFile,
343 * There will be better ways of doing this in the future, for now we'll use
344 * the tried and tested MCview pdb mapping
346 boolean parseSecStr = processSecondaryStructure;
347 if (isPDBFileRegistered(pdbFile))
349 for (SequenceI sq : sequence)
352 while (ds.getDatasetSequence() != null)
354 ds = ds.getDatasetSequence();
357 if (ds.getAnnotation() != null)
359 for (AlignmentAnnotation ala : ds.getAnnotation())
361 // false if any annotation present from this structure
362 // JBPNote this fails for jmol/chimera view because the *file* is
363 // passed, not the structure data ID -
364 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
375 pdb = new PDBfile(addTempFacAnnot, parseSecStr, secStructServices,
377 if (pdb.id != null && pdb.id.trim().length() > 0
378 && AppletFormatAdapter.FILE.equals(protocol))
380 registerPDBFile(pdb.id.trim(), pdbFile);
382 } catch (Exception ex)
384 ex.printStackTrace();
389 for (int s = 0; s < sequence.length; s++)
391 boolean infChain = true;
392 final SequenceI seq = sequence[s];
393 if (targetChains != null && targetChains[s] != null)
396 targetChain = targetChains[s];
398 else if (seq.getName().indexOf("|") > -1)
400 targetChain = seq.getName().substring(
401 seq.getName().lastIndexOf("|") + 1);
402 if (targetChain.length() > 1)
404 if (targetChain.trim().length() == 0)
410 // not a valid chain identifier
421 * Attempt pairwise alignment of the sequence with each chain in the PDB,
422 * and remember the highest scoring chain
425 AlignSeq maxAlignseq = null;
426 String maxChainId = " ";
427 PDBChain maxChain = null;
428 boolean first = true;
429 for (PDBChain chain : pdb.chains)
431 if (targetChain.length() > 0 && !targetChain.equals(chain.id)
434 continue; // don't try to map chains don't match.
436 // TODO: correctly determine sequence type for mixed na/peptide
438 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
439 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
442 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
443 // as.calcScoreMatrix();
444 // as.traceAlignment();
446 if (first || as.maxscore > max
447 || (as.maxscore == max && chain.id.equals(targetChain)))
453 maxChainId = chain.id;
456 if (maxChain == null)
460 final StringBuilder mappingDetails = new StringBuilder(128);
461 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
462 .append(NEWLINE).append("Sequence = ")
463 .append(maxChain.sequence.getSequenceAsString());
464 mappingDetails.append(NEWLINE).append("No of residues = ")
465 .append(maxChain.residues.size()).append(NEWLINE)
467 PrintStream ps = new PrintStream(System.out)
470 public void print(String x)
472 mappingDetails.append(x);
476 public void println()
478 mappingDetails.append(NEWLINE);
482 maxAlignseq.printAlignment(ps);
484 mappingDetails.append(NEWLINE).append("PDB start/end ");
485 mappingDetails.append(String.valueOf(maxAlignseq.seq2start)).append(
487 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
489 mappingDetails.append(NEWLINE).append("SEQ start/end ");
490 mappingDetails.append(
491 String.valueOf(maxAlignseq.seq1start + seq.getStart() - 1))
493 mappingDetails.append(String.valueOf(maxAlignseq.seq1end
494 + seq.getEnd() - 1));
496 maxChain.makeExactMapping(maxAlignseq, seq);
497 jalview.datamodel.Mapping sqmpping = maxAlignseq
498 .getMappingFromS1(false);
499 jalview.datamodel.Mapping omap = new jalview.datamodel.Mapping(
500 sqmpping.getMap().getInverse());
501 maxChain.transferRESNUMFeatures(seq, null);
503 // allocate enough slots to store the mapping from positions in
504 // sequence[s] to the associated chain
505 int[][] mapping = new int[seq.findPosition(seq.getLength()) + 2][2];
511 Atom tmp = maxChain.atoms.elementAt(index);
512 if (resNum != tmp.resNumber && tmp.alignmentMapping != -1)
514 resNum = tmp.resNumber;
515 if (tmp.alignmentMapping >= -1)
517 // TODO (JAL-1836) address root cause: negative residue no in PDB file
518 mapping[tmp.alignmentMapping + 1][0] = tmp.resNumber;
519 mapping[tmp.alignmentMapping + 1][1] = tmp.atomIndex;
524 } while (index < maxChain.atoms.size());
526 if (protocol.equals(jalview.io.AppletFormatAdapter.PASTE))
528 pdbFile = "INLINE" + pdb.id;
530 StructureMapping newMapping = new StructureMapping(seq, pdbFile,
531 pdb.id, maxChainId, mapping, mappingDetails.toString());
532 if (forStructureView)
534 mappings.add(newMapping);
536 maxChain.transferResidueAnnotation(newMapping, sqmpping);
543 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
545 listeners.removeElement(svl);
546 if (svl instanceof SequenceListener)
548 for (int i = 0; i < listeners.size(); i++)
550 if (listeners.elementAt(i) instanceof StructureListener)
552 ((StructureListener) listeners.elementAt(i))
553 .releaseReferences(svl);
558 if (pdbfiles == null)
564 * Remove mappings to the closed listener's PDB files, but first check if
565 * another listener is still interested
567 List<String> pdbs = new ArrayList<String>(Arrays.asList(pdbfiles));
569 StructureListener sl;
570 for (int i = 0; i < listeners.size(); i++)
572 if (listeners.elementAt(i) instanceof StructureListener)
574 sl = (StructureListener) listeners.elementAt(i);
575 for (String pdbfile : sl.getPdbFile())
577 pdbs.remove(pdbfile);
583 * Rebuild the mappings set, retaining only those which are for 'other' PDB
588 List<StructureMapping> tmp = new ArrayList<StructureMapping>();
589 for (StructureMapping sm : mappings)
591 if (!pdbs.contains(sm.pdbfile))
602 * Propagate mouseover of a single position in a structure
608 public void mouseOverStructure(int pdbResNum, String chain, String pdbfile)
610 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
611 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
612 mouseOverStructure(atoms);
616 * Propagate mouseover or selection of multiple positions in a structure
620 public void mouseOverStructure(List<AtomSpec> atoms)
622 if (listeners == null)
624 // old or prematurely sent event
627 boolean hasSequenceListener = false;
628 for (int i = 0; i < listeners.size(); i++)
630 if (listeners.elementAt(i) instanceof SequenceListener)
632 hasSequenceListener = true;
635 if (!hasSequenceListener)
640 SearchResults results = new SearchResults();
641 for (AtomSpec atom : atoms)
643 SequenceI lastseq = null;
645 for (StructureMapping sm : mappings)
647 if (sm.pdbfile.equals(atom.getPdbFile())
648 && sm.pdbchain.equals(atom.getChain()))
650 int indexpos = sm.getSeqPos(atom.getPdbResNum());
651 if (lastipos != indexpos && lastseq != sm.sequence)
653 results.addResult(sm.sequence, indexpos, indexpos);
655 lastseq = sm.sequence;
656 // construct highlighted sequence list
657 for (AlignedCodonFrame acf : seqmappings)
659 acf.markMappedRegion(sm.sequence, indexpos, results);
665 for (Object li : listeners)
667 if (li instanceof SequenceListener)
669 ((SequenceListener) li).highlightSequence(results);
675 * highlight regions associated with a position (indexpos) in seq
678 * the sequence that the mouse over occurred on
680 * the absolute position being mouseovered in seq (0 to seq.length())
682 * the sequence position (if -1, seq.findPosition is called to
683 * resolve the residue number)
685 public void mouseOverSequence(SequenceI seq, int indexpos, int index,
688 boolean hasSequenceListeners = handlingVamsasMo
689 || !seqmappings.isEmpty();
690 SearchResults results = null;
693 index = seq.findPosition(indexpos);
695 for (int i = 0; i < listeners.size(); i++)
697 Object listener = listeners.elementAt(i);
698 if (listener == source)
700 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
701 // Temporary fudge with SequenceListener.getVamsasSource()
704 if (listener instanceof StructureListener)
706 highlightStructure((StructureListener) listener, seq, index);
710 if (listener instanceof SequenceListener)
712 final SequenceListener seqListener = (SequenceListener) listener;
713 if (hasSequenceListeners
714 && seqListener.getVamsasSource() != source)
716 if (relaySeqMappings)
720 results = MappingUtils.buildSearchResults(seq, index,
723 if (handlingVamsasMo)
725 results.addResult(seq, index, index);
728 if (!results.isEmpty())
730 seqListener.highlightSequence(results);
735 else if (listener instanceof VamsasListener && !handlingVamsasMo)
737 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
740 else if (listener instanceof SecondaryStructureListener)
742 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
750 * Send suitable messages to a StructureListener to highlight atoms
751 * corresponding to the given sequence position.
757 protected void highlightStructure(StructureListener sl, SequenceI seq,
760 if (!sl.isListeningFor(seq))
765 List<AtomSpec> atoms = new ArrayList<AtomSpec>();
766 for (StructureMapping sm : mappings)
768 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence())
770 atomNo = sm.getAtomNum(index);
774 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain, sm
775 .getPDBResNum(index), atomNo));
779 sl.highlightAtoms(atoms);
783 * true if a mouse over event from an external (ie Vamsas) source is being
786 boolean handlingVamsasMo = false;
791 * as mouseOverSequence but only route event to SequenceListeners
795 * in an alignment sequence
797 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
800 handlingVamsasMo = true;
801 long msg = sequenceI.hashCode() * (1 + position);
805 mouseOverSequence(sequenceI, position, -1, source);
807 handlingVamsasMo = false;
810 public Annotation[] colourSequenceFromStructure(SequenceI seq,
814 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
815 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
817 * Annotation [] annotations = new Annotation[seq.getLength()];
819 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
820 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
821 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
823 * for (int j = 0; j < mappings.length; j++) {
825 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
826 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
827 * System.out.println(pdbid+" "+mappings[j].getPdbId() +"
828 * "+mappings[j].pdbfile);
830 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
831 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
833 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
834 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
835 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
836 * mappings[j].pdbfile); }
838 * annotations[index] = new Annotation("X",null,' ',0,col); } return
839 * annotations; } } } }
841 * return annotations;
845 public void structureSelectionChanged()
849 public void sequenceSelectionChanged()
853 public void sequenceColoursChanged(Object source)
855 StructureListener sl;
856 for (int i = 0; i < listeners.size(); i++)
858 if (listeners.elementAt(i) instanceof StructureListener)
860 sl = (StructureListener) listeners.elementAt(i);
861 sl.updateColours(source);
866 public StructureMapping[] getMapping(String pdbfile)
868 List<StructureMapping> tmp = new ArrayList<StructureMapping>();
869 for (StructureMapping sm : mappings)
871 if (sm.pdbfile.equals(pdbfile))
876 return tmp.toArray(new StructureMapping[tmp.size()]);
880 * Returns a readable description of all mappings for the given pdbfile to any
881 * of the given sequences
887 public String printMappings(String pdbfile, List<SequenceI> seqs)
889 if (pdbfile == null || seqs == null || seqs.isEmpty())
894 StringBuilder sb = new StringBuilder(64);
895 for (StructureMapping sm : mappings)
897 if (sm.pdbfile.equals(pdbfile) && seqs.contains(sm.sequence))
899 sb.append(sm.mappingDetails);
901 // separator makes it easier to read multiple mappings
902 sb.append("=====================");
908 return sb.toString();
912 * Remove the given mapping
916 public void deregisterMapping(AlignedCodonFrame acf)
920 boolean removed = seqmappings.remove(acf);
921 if (removed && seqmappings.isEmpty())
923 System.out.println("All mappings removed");
929 * Add each of the given codonFrames to the stored set, if not aready present.
933 public void registerMappings(Set<AlignedCodonFrame> set)
937 for (AlignedCodonFrame acf : set)
939 registerMapping(acf);
945 * Add the given mapping to the stored set, unless already stored.
947 public void registerMapping(AlignedCodonFrame acf)
951 if (!seqmappings.contains(acf))
953 seqmappings.add(acf);
959 * Resets this object to its initial state by removing all registered
960 * listeners, codon mappings, PDB file mappings
962 public void resetAll()
964 if (mappings != null)
968 if (seqmappings != null)
972 if (sel_listeners != null)
974 sel_listeners.clear();
976 if (listeners != null)
980 if (commandListeners != null)
982 commandListeners.clear();
984 if (view_listeners != null)
986 view_listeners.clear();
988 if (pdbFileNameId != null)
990 pdbFileNameId.clear();
992 if (pdbIdFileName != null)
994 pdbIdFileName.clear();
998 public void addSelectionListener(SelectionListener selecter)
1000 if (!sel_listeners.contains(selecter))
1002 sel_listeners.add(selecter);
1006 public void removeSelectionListener(SelectionListener toremove)
1008 if (sel_listeners.contains(toremove))
1010 sel_listeners.remove(toremove);
1014 public synchronized void sendSelection(
1015 jalview.datamodel.SequenceGroup selection,
1016 jalview.datamodel.ColumnSelection colsel, SelectionSource source)
1018 for (SelectionListener slis : sel_listeners)
1022 slis.selection(selection, colsel, source);
1027 Vector<AlignmentViewPanelListener> view_listeners = new Vector<AlignmentViewPanelListener>();
1029 public synchronized void sendViewPosition(
1030 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1031 int startSeq, int endSeq)
1034 if (view_listeners != null && view_listeners.size() > 0)
1036 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1038 while (listeners.hasMoreElements())
1040 AlignmentViewPanelListener slis = listeners.nextElement();
1043 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1051 * release all references associated with this manager provider
1053 * @param jalviewLite
1055 public static void release(StructureSelectionManagerProvider jalviewLite)
1057 // synchronized (instances)
1059 if (instances == null)
1063 StructureSelectionManager mnger = (instances.get(jalviewLite));
1066 instances.remove(jalviewLite);
1070 } catch (Throwable x)
1077 public void registerPDBEntry(PDBEntry pdbentry)
1079 if (pdbentry.getFile() != null
1080 && pdbentry.getFile().trim().length() > 0)
1082 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1086 public void addCommandListener(CommandListener cl)
1088 if (!commandListeners.contains(cl))
1090 commandListeners.add(cl);
1094 public boolean hasCommandListener(CommandListener cl)
1096 return this.commandListeners.contains(cl);
1099 public boolean removeCommandListener(CommandListener l)
1101 return commandListeners.remove(l);
1105 * Forward a command to any command listeners (except for the command's
1109 * the command to be broadcast (in its form after being performed)
1111 * if true, the command was being 'undone'
1114 public void commandPerformed(CommandI command, boolean undo,
1115 VamsasSource source)
1117 for (CommandListener listener : commandListeners)
1119 listener.mirrorCommand(command, undo, this, source);
1124 * Returns a new CommandI representing the given command as mapped to the
1125 * given sequences. If no mapping could be made, or the command is not of a
1126 * mappable kind, returns null.
1134 public CommandI mapCommand(CommandI command, boolean undo,
1135 final AlignmentI mapTo, char gapChar)
1137 if (command instanceof EditCommand)
1139 return MappingUtils.mapEditCommand((EditCommand) command, undo,
1140 mapTo, gapChar, seqmappings);
1142 else if (command instanceof OrderCommand)
1144 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1145 mapTo, seqmappings);