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
519 mapping[tmp.alignmentMapping + 1][0] = tmp.resNumber;
520 mapping[tmp.alignmentMapping + 1][1] = tmp.atomIndex;
525 } while (index < maxChain.atoms.size());
527 if (protocol.equals(jalview.io.AppletFormatAdapter.PASTE))
529 pdbFile = "INLINE" + pdb.id;
531 StructureMapping newMapping = new StructureMapping(seq, pdbFile,
532 pdb.id, maxChainId, mapping, mappingDetails.toString());
533 if (forStructureView)
535 mappings.add(newMapping);
537 maxChain.transferResidueAnnotation(newMapping, sqmpping);
544 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
546 listeners.removeElement(svl);
547 if (svl instanceof SequenceListener)
549 for (int i = 0; i < listeners.size(); i++)
551 if (listeners.elementAt(i) instanceof StructureListener)
553 ((StructureListener) listeners.elementAt(i))
554 .releaseReferences(svl);
559 if (pdbfiles == null)
565 * Remove mappings to the closed listener's PDB files, but first check if
566 * another listener is still interested
568 List<String> pdbs = new ArrayList<String>(Arrays.asList(pdbfiles));
570 StructureListener sl;
571 for (int i = 0; i < listeners.size(); i++)
573 if (listeners.elementAt(i) instanceof StructureListener)
575 sl = (StructureListener) listeners.elementAt(i);
576 for (String pdbfile : sl.getPdbFile())
578 pdbs.remove(pdbfile);
584 * Rebuild the mappings set, retaining only those which are for 'other' PDB
589 List<StructureMapping> tmp = new ArrayList<StructureMapping>();
590 for (StructureMapping sm : mappings)
592 if (!pdbs.contains(sm.pdbfile))
603 * Propagate mouseover of a single position in a structure
609 public void mouseOverStructure(int pdbResNum, String chain, String pdbfile)
611 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
612 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
613 mouseOverStructure(atoms);
617 * Propagate mouseover or selection of multiple positions in a structure
621 public void mouseOverStructure(List<AtomSpec> atoms)
623 if (listeners == null)
625 // old or prematurely sent event
628 boolean hasSequenceListener = false;
629 for (int i = 0; i < listeners.size(); i++)
631 if (listeners.elementAt(i) instanceof SequenceListener)
633 hasSequenceListener = true;
636 if (!hasSequenceListener)
641 SearchResults results = new SearchResults();
642 for (AtomSpec atom : atoms)
644 SequenceI lastseq = null;
646 for (StructureMapping sm : mappings)
648 if (sm.pdbfile.equals(atom.getPdbFile())
649 && sm.pdbchain.equals(atom.getChain()))
651 int indexpos = sm.getSeqPos(atom.getPdbResNum());
652 if (lastipos != indexpos && lastseq != sm.sequence)
654 results.addResult(sm.sequence, indexpos, indexpos);
656 lastseq = sm.sequence;
657 // construct highlighted sequence list
658 for (AlignedCodonFrame acf : seqmappings)
660 acf.markMappedRegion(sm.sequence, indexpos, results);
666 for (Object li : listeners)
668 if (li instanceof SequenceListener)
670 ((SequenceListener) li).highlightSequence(results);
676 * highlight regions associated with a position (indexpos) in seq
679 * the sequence that the mouse over occurred on
681 * the absolute position being mouseovered in seq (0 to seq.length())
683 * the sequence position (if -1, seq.findPosition is called to
684 * resolve the residue number)
686 public void mouseOverSequence(SequenceI seq, int indexpos, int index,
689 boolean hasSequenceListeners = handlingVamsasMo
690 || !seqmappings.isEmpty();
691 SearchResults results = null;
694 index = seq.findPosition(indexpos);
696 for (int i = 0; i < listeners.size(); i++)
698 Object listener = listeners.elementAt(i);
699 if (listener == source)
701 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
702 // Temporary fudge with SequenceListener.getVamsasSource()
705 if (listener instanceof StructureListener)
707 highlightStructure((StructureListener) listener, seq, index);
711 if (listener instanceof SequenceListener)
713 final SequenceListener seqListener = (SequenceListener) listener;
714 if (hasSequenceListeners
715 && seqListener.getVamsasSource() != source)
717 if (relaySeqMappings)
721 results = MappingUtils.buildSearchResults(seq, index,
724 if (handlingVamsasMo)
726 results.addResult(seq, index, index);
729 if (!results.isEmpty())
731 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(jalview.util.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 * Remove the given mapping
917 public void deregisterMapping(AlignedCodonFrame acf)
921 boolean removed = seqmappings.remove(acf);
922 if (removed && seqmappings.isEmpty())
924 System.out.println("All mappings removed");
930 * Add each of the given codonFrames to the stored set, if not aready present.
934 public void registerMappings(Set<AlignedCodonFrame> set)
938 for (AlignedCodonFrame acf : set)
940 registerMapping(acf);
946 * Add the given mapping to the stored set, unless already stored.
948 public void registerMapping(AlignedCodonFrame acf)
952 if (!seqmappings.contains(acf))
954 seqmappings.add(acf);
960 * Resets this object to its initial state by removing all registered
961 * listeners, codon mappings, PDB file mappings
963 public void resetAll()
965 if (mappings != null)
969 if (seqmappings != null)
973 if (sel_listeners != null)
975 sel_listeners.clear();
977 if (listeners != null)
981 if (commandListeners != null)
983 commandListeners.clear();
985 if (view_listeners != null)
987 view_listeners.clear();
989 if (pdbFileNameId != null)
991 pdbFileNameId.clear();
993 if (pdbIdFileName != null)
995 pdbIdFileName.clear();
999 public void addSelectionListener(SelectionListener selecter)
1001 if (!sel_listeners.contains(selecter))
1003 sel_listeners.add(selecter);
1007 public void removeSelectionListener(SelectionListener toremove)
1009 if (sel_listeners.contains(toremove))
1011 sel_listeners.remove(toremove);
1015 public synchronized void sendSelection(
1016 jalview.datamodel.SequenceGroup selection,
1017 jalview.datamodel.ColumnSelection colsel, SelectionSource source)
1019 for (SelectionListener slis : sel_listeners)
1023 slis.selection(selection, colsel, source);
1028 Vector<AlignmentViewPanelListener> view_listeners = new Vector<AlignmentViewPanelListener>();
1030 public synchronized void sendViewPosition(
1031 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1032 int startSeq, int endSeq)
1035 if (view_listeners != null && view_listeners.size() > 0)
1037 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1039 while (listeners.hasMoreElements())
1041 AlignmentViewPanelListener slis = listeners.nextElement();
1044 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1052 * release all references associated with this manager provider
1054 * @param jalviewLite
1056 public static void release(StructureSelectionManagerProvider jalviewLite)
1058 // synchronized (instances)
1060 if (instances == null)
1064 StructureSelectionManager mnger = (instances.get(jalviewLite));
1067 instances.remove(jalviewLite);
1071 } catch (Throwable x)
1078 public void registerPDBEntry(PDBEntry pdbentry)
1080 if (pdbentry.getFile() != null
1081 && pdbentry.getFile().trim().length() > 0)
1083 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1087 public void addCommandListener(CommandListener cl)
1089 if (!commandListeners.contains(cl))
1091 commandListeners.add(cl);
1095 public boolean hasCommandListener(CommandListener cl)
1097 return this.commandListeners.contains(cl);
1100 public boolean removeCommandListener(CommandListener l)
1102 return commandListeners.remove(l);
1106 * Forward a command to any command listeners (except for the command's
1110 * the command to be broadcast (in its form after being performed)
1112 * if true, the command was being 'undone'
1115 public void commandPerformed(CommandI command, boolean undo,
1116 VamsasSource source)
1118 for (CommandListener listener : commandListeners)
1120 listener.mirrorCommand(command, undo, this, source);
1125 * Returns a new CommandI representing the given command as mapped to the
1126 * given sequences. If no mapping could be made, or the command is not of a
1127 * mappable kind, returns null.
1135 public CommandI mapCommand(CommandI command, boolean undo,
1136 final AlignmentI mapTo, char gapChar)
1138 if (command instanceof EditCommand)
1140 return MappingUtils.mapEditCommand((EditCommand) command, undo,
1141 mapTo, gapChar, seqmappings);
1143 else if (command instanceof OrderCommand)
1145 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1146 mapTo, seqmappings);