*/
package MCview;
-import java.io.*;
-import java.util.*;
+import jalview.analysis.AlignSeq;
+import jalview.appletgui.AlignmentPanel;
+import jalview.appletgui.FeatureRenderer;
+import jalview.appletgui.SequenceRenderer;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.structure.AtomSpec;
+import jalview.structure.StructureListener;
+import jalview.structure.StructureMapping;
+import jalview.structure.StructureSelectionManager;
+import jalview.util.MessageManager;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Event;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Image;
// JBPNote TODO: This class is quite noisy - needs proper log.info/log.debug
-import java.awt.*;
-import java.awt.event.*;
-
-import jalview.analysis.*;
-import jalview.datamodel.*;
-
-import jalview.appletgui.*;
-import jalview.structure.*;
-import jalview.util.MessageManager;
+import java.awt.Panel;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.io.PrintStream;
+import java.util.List;
+import java.util.Vector;
public class AppletPDBCanvas extends Panel implements MouseListener,
MouseMotionListener, StructureListener
pdb = ssm.setMapping(seq, chains, pdbentry.getFile(), protocol);
if (protocol.equals(jalview.io.AppletFormatAdapter.PASTE))
+ {
pdbentry.setFile("INLINE" + pdb.id);
+ }
} catch (Exception ex)
{
{
mappingDetails.append("\n\nPDB Sequence is :\nSequence = "
- + ((PDBChain) pdb.chains.elementAt(i)).sequence
+ + pdb.chains.elementAt(i).sequence
.getSequenceAsString());
mappingDetails.append("\nNo of residues = "
- + ((PDBChain) pdb.chains.elementAt(i)).residues.size()
+ + pdb.chains.elementAt(i).residues.size()
+ "\n\n");
// Now lets compare the sequences to get
// Align the sequence to the pdb
// TODO: DNa/Pep switch
AlignSeq as = new AlignSeq(sequence,
- ((PDBChain) pdb.chains.elementAt(i)).sequence,
- ((PDBChain) pdb.chains.elementAt(i)).isNa ? AlignSeq.DNA
+ pdb.chains.elementAt(i).sequence,
+ pdb.chains.elementAt(i).isNa ? AlignSeq.DNA
: AlignSeq.PEP);
as.calcScoreMatrix();
as.traceAlignment();
mappingDetails.append("\nSEQ start/end " + seqstart + " " + seqend);
}
- mainchain = (PDBChain) pdb.chains.elementAt(maxchain);
+ mainchain = pdb.chains.elementAt(maxchain);
mainchain.pdbstart = pdbstart;
mainchain.pdbend = pdbend;
for (int ii = 0; ii < pdb.chains.size(); ii++)
{
- if (((PDBChain) pdb.chains.elementAt(ii)).isVisible)
+ if (pdb.chains.elementAt(ii).isVisible)
{
- Vector tmp = ((PDBChain) pdb.chains.elementAt(ii)).bonds;
+ Vector tmp = pdb.chains.elementAt(ii).bonds;
for (int i = 0; i < tmp.size(); i++)
{
for (int ii = 0; ii < pdb.chains.size(); ii++)
{
- if (((PDBChain) pdb.chains.elementAt(ii)).isVisible)
+ if (pdb.chains.elementAt(ii).isVisible)
{
- Vector bonds = ((PDBChain) pdb.chains.elementAt(ii)).bonds;
+ Vector bonds = pdb.chains.elementAt(ii).bonds;
for (int i = 0; i < bonds.size(); i++)
{
}
}
- width[0] = (float) Math.abs(max[0] - min[0]);
- width[1] = (float) Math.abs(max[1] - min[1]);
- width[2] = (float) Math.abs(max[2] - min[2]);
+ width[0] = Math.abs(max[0] - min[0]);
+ width[1] = Math.abs(max[1] - min[1]);
+ width[2] = Math.abs(max[2] - min[2]);
maxwidth = width[0];
// Find centre coordinate
for (int ii = 0; ii < pdb.chains.size(); ii++)
{
- if (((PDBChain) pdb.chains.elementAt(ii)).isVisible)
+ if (pdb.chains.elementAt(ii).isVisible)
{
- Vector bonds = ((PDBChain) pdb.chains.elementAt(ii)).bonds;
+ Vector bonds = pdb.chains.elementAt(ii).bonds;
bsize += bonds.size();
{
for (int ii = 0; ii < pdb.chains.size(); ii++)
{
- chain = (PDBChain) pdb.chains.elementAt(ii);
+ chain = pdb.chains.elementAt(ii);
for (int i = 0; i < chain.bonds.size(); i++)
{
repaint();
if (foundchain != -1)
{
- PDBChain chain = (PDBChain) pdb.chains.elementAt(foundchain);
+ PDBChain chain = pdb.chains.elementAt(foundchain);
if (chain == mainchain)
{
if (fatom.alignmentMapping != -1)
PDBChain chain = null;
if (foundchain != -1)
{
- chain = (PDBChain) pdb.chains.elementAt(foundchain);
+ chain = pdb.chains.elementAt(foundchain);
if (chain == mainchain)
{
mouseOverStructure(fatom.resNumber, chain.id);
if ((evt.getModifiers() & Event.META_MASK) != 0)
{
- objmat.rotatez((float) ((mx - omx)));
+ objmat.rotatez(((mx - omx)));
}
else
{
- objmat.rotatex((float) ((omy - my)));
- objmat.rotatey((float) ((omx - mx)));
+ objmat.rotatex(((omy - my)));
+ objmat.rotatey(((omx - mx)));
}
// Alter the bonds
for (int ii = 0; ii < pdb.chains.size(); ii++)
{
- Vector bonds = ((PDBChain) pdb.chains.elementAt(ii)).bonds;
+ Vector bonds = pdb.chains.elementAt(ii).bonds;
for (int i = 0; i < bonds.size(); i++)
{
for (int ii = 0; ii < pdb.chains.size(); ii++)
{
- PDBChain chain = (PDBChain) pdb.chains.elementAt(ii);
+ PDBChain chain = pdb.chains.elementAt(ii);
if (chain.isVisible)
{
- Vector bonds = ((PDBChain) pdb.chains.elementAt(ii)).bonds;
+ Vector bonds = pdb.chains.elementAt(ii).bonds;
for (int i = 0; i < bonds.size(); i++)
{
for (int ii = 0; ii < pdb.chains.size(); ii++)
{
- PDBChain chain = (PDBChain) pdb.chains.elementAt(ii);
+ PDBChain chain = pdb.chains.elementAt(ii);
int truex;
Bond tmpBond = null;
if (chain.isVisible)
{
- Vector bonds = ((PDBChain) pdb.chains.elementAt(ii)).bonds;
+ Vector bonds = pdb.chains.elementAt(ii).bonds;
for (int i = 0; i < bonds.size(); i++)
{
if (fatom != null) // )&& chain.ds != null)
{
- chain = (PDBChain) pdb.chains.elementAt(foundchain);
+ chain = pdb.chains.elementAt(foundchain);
}
}
{
for (int ii = 0; ii < pdb.chains.size(); ii++)
{
- PDBChain chain = (PDBChain) pdb.chains.elementAt(ii);
+ PDBChain chain = pdb.chains.elementAt(ii);
chain.isVisible = b;
}
mainchain.isVisible = true;
public void mouseOverStructure(int pdbResNum, String chain)
{
if (lastMessage == null || !lastMessage.equals(pdbResNum + chain))
+ {
ssm.mouseOverStructure(pdbResNum, chain, pdbentry.getFile());
+ }
lastMessage = pdbResNum + chain;
}
StringBuffer eval = new StringBuffer();
- public void highlightAtom(int atomIndex, int pdbResNum, String chain,
- String pdbfile)
+ /**
+ * Highlight the specified atoms in the structure.
+ *
+ * @param atoms
+ */
+ @Override
+ public void highlightAtoms(List<AtomSpec> atoms)
{
if (!seqColoursReady)
{
return;
}
-
- if (highlightRes != null && highlightRes.contains((atomIndex - 1) + ""))
+ for (AtomSpec atom : atoms)
{
- return;
+ int atomIndex = atom.getAtomIndex();
+
+ if (highlightRes != null
+ && highlightRes.contains((atomIndex - 1) + ""))
+ {
+ continue;
+ }
+
+ highlightAtom(atomIndex);
}
+ redrawneeded = true;
+ repaint();
+ }
+
+ /**
+ * @param atomIndex
+ */
+ protected void highlightAtom(int atomIndex)
+ {
int index = -1;
Bond tmpBond;
for (index = 0; index < mainchain.bonds.size(); index++)
break;
}
}
-
- redrawneeded = true;
- repaint();
}
public Color getColour(int atomIndex, int pdbResNum, String chain,
*/
package MCview;
-import java.io.*;
-import java.util.*;
-
+import jalview.analysis.AlignSeq;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignmentPanel;
+import jalview.gui.FeatureRenderer;
+import jalview.gui.SequenceRenderer;
+import jalview.structure.AtomSpec;
+import jalview.structure.StructureListener;
+import jalview.structure.StructureMapping;
+import jalview.structure.StructureSelectionManager;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Event;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
// JBPNote TODO: This class is quite noisy - needs proper log.info/log.debug
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-
-import jalview.analysis.*;
-import jalview.datamodel.*;
-import jalview.gui.*;
-import jalview.structure.*;
+import java.awt.Image;
+import java.awt.RenderingHints;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.io.PrintStream;
+import java.util.List;
+import java.util.Vector;
+
+import javax.swing.JPanel;
+import javax.swing.ToolTipManager;
public class PDBCanvas extends JPanel implements MouseListener,
MouseMotionListener, StructureListener
pdb = ssm.setMapping(seq, chains, pdbentry.getFile(), protocol);
if (protocol.equals(jalview.io.AppletFormatAdapter.PASTE))
+ {
pdbentry.setFile("INLINE" + pdb.id);
+ }
} catch (Exception ex)
{
{
mappingDetails.append("\n\nPDB Sequence is :\nSequence = "
- + ((PDBChain) pdb.chains.elementAt(i)).sequence
+ + pdb.chains.elementAt(i).sequence
.getSequenceAsString());
mappingDetails.append("\nNo of residues = "
- + ((PDBChain) pdb.chains.elementAt(i)).residues.size()
+ + pdb.chains.elementAt(i).residues.size()
+ "\n\n");
// Now lets compare the sequences to get
// the start and end points.
// Align the sequence to the pdb
AlignSeq as = new AlignSeq(sequence,
- ((PDBChain) pdb.chains.elementAt(i)).sequence, "pep");
+ pdb.chains.elementAt(i).sequence, "pep");
as.calcScoreMatrix();
as.traceAlignment();
PrintStream ps = new PrintStream(System.out)
mappingDetails.append("\nSEQ start/end " + seqstart + " " + seqend);
}
- mainchain = (PDBChain) pdb.chains.elementAt(maxchain);
+ mainchain = pdb.chains.elementAt(maxchain);
mainchain.pdbstart = pdbstart;
mainchain.pdbend = pdbend;
for (int ii = 0; ii < pdb.chains.size(); ii++)
{
- if (((PDBChain) pdb.chains.elementAt(ii)).isVisible)
+ if (pdb.chains.elementAt(ii).isVisible)
{
- Vector tmp = ((PDBChain) pdb.chains.elementAt(ii)).bonds;
+ Vector tmp = pdb.chains.elementAt(ii).bonds;
for (int i = 0; i < tmp.size(); i++)
{
for (int ii = 0; ii < pdb.chains.size(); ii++)
{
- if (((PDBChain) pdb.chains.elementAt(ii)).isVisible)
+ if (pdb.chains.elementAt(ii).isVisible)
{
- Vector bonds = ((PDBChain) pdb.chains.elementAt(ii)).bonds;
+ Vector bonds = pdb.chains.elementAt(ii).bonds;
for (int i = 0; i < bonds.size(); i++)
{
* System.out.println("zmax " + max[2] + " min " + min[2]);
*/
- width[0] = (float) Math.abs(max[0] - min[0]);
- width[1] = (float) Math.abs(max[1] - min[1]);
- width[2] = (float) Math.abs(max[2] - min[2]);
+ width[0] = Math.abs(max[0] - min[0]);
+ width[1] = Math.abs(max[1] - min[1]);
+ width[2] = Math.abs(max[2] - min[2]);
maxwidth = width[0];
// Find centre coordinate
for (int ii = 0; ii < pdb.chains.size(); ii++)
{
- if (((PDBChain) pdb.chains.elementAt(ii)).isVisible)
+ if (pdb.chains.elementAt(ii).isVisible)
{
- Vector bonds = ((PDBChain) pdb.chains.elementAt(ii)).bonds;
+ Vector bonds = pdb.chains.elementAt(ii).bonds;
bsize += bonds.size();
{
for (int ii = 0; ii < pdb.chains.size(); ii++)
{
- chain = (PDBChain) pdb.chains.elementAt(ii);
+ chain = pdb.chains.elementAt(ii);
for (int i = 0; i < chain.bonds.size(); i++)
{
repaint();
if (foundchain != -1)
{
- PDBChain chain = (PDBChain) pdb.chains.elementAt(foundchain);
+ PDBChain chain = pdb.chains.elementAt(foundchain);
if (chain == mainchain)
{
if (fatom.alignmentMapping != -1)
PDBChain chain = null;
if (foundchain != -1)
{
- chain = (PDBChain) pdb.chains.elementAt(foundchain);
+ chain = pdb.chains.elementAt(foundchain);
if (chain == mainchain)
{
mouseOverStructure(fatom.resNumber, chain.id);
if ((evt.getModifiers() & Event.META_MASK) != 0)
{
- objmat.rotatez((float) ((mx - omx)));
+ objmat.rotatez(((mx - omx)));
}
else
{
- objmat.rotatex((float) ((my - omy)));
- objmat.rotatey((float) ((omx - mx)));
+ objmat.rotatex(((my - omy)));
+ objmat.rotatey(((omx - mx)));
}
// Alter the bonds
for (int ii = 0; ii < pdb.chains.size(); ii++)
{
- Vector bonds = ((PDBChain) pdb.chains.elementAt(ii)).bonds;
+ Vector bonds = pdb.chains.elementAt(ii).bonds;
for (int i = 0; i < bonds.size(); i++)
{
for (int ii = 0; ii < pdb.chains.size(); ii++)
{
- PDBChain chain = (PDBChain) pdb.chains.elementAt(ii);
+ PDBChain chain = pdb.chains.elementAt(ii);
if (chain.isVisible)
{
- Vector bonds = ((PDBChain) pdb.chains.elementAt(ii)).bonds;
+ Vector bonds = pdb.chains.elementAt(ii).bonds;
for (int i = 0; i < bonds.size(); i++)
{
for (int ii = 0; ii < pdb.chains.size(); ii++)
{
- PDBChain chain = (PDBChain) pdb.chains.elementAt(ii);
+ PDBChain chain = pdb.chains.elementAt(ii);
int truex;
Bond tmpBond = null;
if (chain.isVisible)
{
- Vector bonds = ((PDBChain) pdb.chains.elementAt(ii)).bonds;
+ Vector bonds = pdb.chains.elementAt(ii).bonds;
for (int i = 0; i < bonds.size(); i++)
{
if (fatom != null) // )&& chain.ds != null)
{
- chain = (PDBChain) pdb.chains.elementAt(foundchain);
+ chain = pdb.chains.elementAt(foundchain);
}
}
{
for (int ii = 0; ii < pdb.chains.size(); ii++)
{
- PDBChain chain = (PDBChain) pdb.chains.elementAt(ii);
+ PDBChain chain = pdb.chains.elementAt(ii);
chain.isVisible = b;
}
mainchain.isVisible = true;
public void mouseOverStructure(int pdbResNum, String chain)
{
if (lastMessage == null || !lastMessage.equals(pdbResNum + chain))
+ {
ssm.mouseOverStructure(pdbResNum, chain, pdbentry.getFile());
+ }
lastMessage = pdbResNum + chain;
}
StringBuffer eval = new StringBuffer();
- public void highlightAtom(int atomIndex, int pdbResNum, String chain,
- String pdbfile)
+ /**
+ * Highlight the specified atoms in the structure.
+ *
+ * @param atoms
+ */
+ @Override
+ public void highlightAtoms(List<AtomSpec> atoms)
{
if (!seqColoursReady)
{
return;
}
- if (highlightRes != null && highlightRes.contains((atomIndex - 1) + ""))
+ for (AtomSpec atom : atoms)
{
- return;
+ int atomIndex = atom.getAtomIndex();
+ if (highlightRes != null
+ && highlightRes.contains((atomIndex - 1) + ""))
+ {
+ continue;
+ }
+
+ highlightAtom(atomIndex);
}
+ redrawneeded = true;
+ repaint();
+ }
+
+ /**
+ * Highlight the atom at the specified index.
+ *
+ * @param atomIndex
+ */
+ protected void highlightAtom(int atomIndex)
+ {
int index = -1;
Bond tmpBond;
for (index = 0; index < mainchain.bonds.size(); index++)
break;
}
}
-
- redrawneeded = true;
- repaint();
}
public Color getColour(int atomIndex, int pdbResNum, String chain,
import jalview.structure.SelectionSource;
import jalview.structure.SequenceListener;
import jalview.structure.StructureSelectionManager;
+import jalview.structure.VamsasSource;
import jalview.util.MessageManager;
import java.awt.BorderLayout;
}
+ @Override
+ public VamsasSource getVamsasSource()
+ {
+ return this.ap == null ? null : this.ap.av;
+ }
+
public void updateColours(SequenceI seq, int index)
{
System.out.println("update the seqPanel colours");
import jalview.datamodel.Sequence;
import jalview.datamodel.SequenceFeature;
import jalview.datamodel.SequenceI;
+import jalview.util.ReverseListIterator;
+import jalview.util.StringUtils;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.Hashtable;
+import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
+import java.util.Map;
/**
*
}
/**
- * Add the given edit command to the stored list of commands.
+ * Add the given edit command to the stored list of commands. If simply
+ * expanding the range of the last command added, then modify it instead of
+ * adding a new command.
*
* @param e
*/
- protected void addEdit(Edit e)
+ public void addEdit(Edit e)
{
- edits.add(e);
+ if (!expandEdit(edits, e))
+ {
+ edits.add(e);
+ }
+ }
+
+ /**
+ * Returns true if the new edit is incorporated by updating (expanding the
+ * range of) the last edit on the list, else false. We can 'expand' the last
+ * edit if the new one is the same action, on the same sequences, and acts on
+ * a contiguous range. This is the case where a mouse drag generates a series
+ * of contiguous gap insertions or deletions.
+ *
+ * @param edits
+ * @param e
+ * @return
+ */
+ protected static boolean expandEdit(List<Edit> edits, Edit e)
+ {
+ if (edits == null || edits.isEmpty())
+ {
+ return false;
+ }
+ Edit lastEdit = edits.get(edits.size() - 1);
+ Action action = e.command;
+ if (lastEdit.command != action)
+ {
+ return false;
+ }
+
+ /*
+ * Both commands must act on the same sequences - compare the underlying
+ * dataset sequences, rather than the aligned sequences, which change as
+ * they are edited.
+ */
+ if (lastEdit.seqs.length != e.seqs.length)
+ {
+ return false;
+ }
+ for (int i = 0; i < e.seqs.length; i++)
+ {
+ if (lastEdit.seqs[i].getDatasetSequence() != e.seqs[i]
+ .getDatasetSequence())
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Check a contiguous edit; either
+ * <ul>
+ * <li>a new Insert <n> positions to the right of the last <insert n>, or</li>
+ * <li>a new Delete <n> gaps which is <n> positions to the left of the last
+ * delete.</li>
+ * </ul>
+ */
+ boolean contiguous = (action == Action.INSERT_GAP && e.position == lastEdit.position
+ + lastEdit.number)
+ || (action == Action.DELETE_GAP && e.position + e.number == lastEdit.position);
+ if (contiguous)
+ {
+ /*
+ * We are just expanding the range of the last edit. For delete gap, also
+ * moving the start position left.
+ */
+ lastEdit.number += e.number;
+ lastEdit.seqs = e.seqs;
+ if (action == Action.DELETE_GAP)
+ {
+ lastEdit.position--;
+ }
+ return true;
+ }
+ return false;
}
/**
edit.fullAlignmentHeight = true;
}
- edits.add(edit);
+ addEdit(edit);
+
+ if (performEdit)
+ {
+ performEdit(edit, views);
+ }
+ }
+
+ /**
+ * Overloaded method that accepts an Edit object with additional parameters.
+ *
+ * @param edit
+ * @param al
+ * @param performEdit
+ * @param views
+ */
+ final public void appendEdit(Edit edit, AlignmentI al,
+ boolean performEdit, AlignmentI[] views)
+ {
+ if (al.getHeight() == edit.seqs.length)
+ {
+ edit.al = al;
+ edit.fullAlignmentHeight = true;
+ }
+
+ addEdit(edit);
if (performEdit)
{
* @param commandIndex
* @param views
*/
- final void performEdit(int commandIndex, AlignmentI[] views)
+ public final void performEdit(int commandIndex, AlignmentI[] views)
{
ListIterator<Edit> iterator = edits.listIterator(commandIndex);
while (iterator.hasNext())
* @param edit
* @param views
*/
- protected void performEdit(Edit edit, AlignmentI[] views)
+ protected static void performEdit(Edit edit, AlignmentI[] views)
{
switch (edit.command)
{
*
* @param command
*/
- final private void insertGap(Edit command)
+ final private static void insertGap(Edit command)
{
for (int s = 0; s < command.seqs.length; s++)
{
- command.seqs[s].insertCharAt(command.position, command.number,
- command.gapChar);
+ command.seqs[s].insertCharAt(command.position,
+ command.number, command.gapChar);
// System.out.println("pos: "+command.position+" number: "+command.number);
}
*
* @param command
*/
- final private void deleteGap(Edit command)
+ final static private void deleteGap(Edit command)
{
for (int s = 0; s < command.seqs.length; s++)
{
* @param command
* @param views
*/
- void cut(Edit command, AlignmentI[] views)
+ static void cut(Edit command, AlignmentI[] views)
{
boolean seqDeleted = false;
command.string = new char[command.seqs.length][];
* @param command
* @param views
*/
- void paste(Edit command, AlignmentI[] views)
+ static void paste(Edit command, AlignmentI[] views)
{
StringBuffer tmp;
boolean newDSNeeded;
command.string = null;
}
- void replace(Edit command)
+ static void replace(Edit command)
{
StringBuffer tmp;
String oldstring;
}
}
- final void adjustAnnotations(Edit command, boolean insert,
+ final static void adjustAnnotations(Edit command, boolean insert,
boolean modifyVisibility, AlignmentI[] views)
{
AlignmentAnnotation[] annotations = null;
}
}
- final void adjustFeatures(Edit command, int index, int i, int j,
+ final static void adjustFeatures(Edit command, int index, int i, int j,
boolean insert)
{
SequenceI seq = command.seqs[index];
}
- class Edit
+ /**
+ * Returns the list of edit commands wrapped by this object.
+ *
+ * @return
+ */
+ public List<Edit> getEdits()
+ {
+ return this.edits;
+ }
+
+ /**
+ * Returns a map whose keys are the dataset sequences, and values their
+ * aligned sequences before the command edit list was applied. The aligned
+ * sequences are copies, which may be updated without affecting the originals.
+ *
+ * The command holds references to the aligned sequences (after editing). If
+ * the command is an 'undo',then the prior state is simply the aligned state.
+ * Otherwise, we have to derive the prior state by working backwards through
+ * the edit list to infer the aligned sequences before editing.
+ *
+ * Note: an alternative solution would be to cache the 'before' state of each
+ * edit, but this would be expensive in space in the common case that the
+ * original is never needed (edits are not mirrored).
+ *
+ * @return
+ * @throws IllegalStateException
+ * on detecting an edit command of a type that can't be unwound
+ */
+ public Map<SequenceI, SequenceI> priorState(boolean forUndo)
+ {
+ Map<SequenceI, SequenceI> result = new HashMap<SequenceI, SequenceI>();
+ if (getEdits() == null)
+ {
+ return result;
+ }
+ if (forUndo)
+ {
+ for (Edit e : getEdits())
+ {
+ for (SequenceI seq : e.getSequences())
+ {
+ SequenceI ds = seq.getDatasetSequence();
+ SequenceI preEdit = result.get(ds);
+ if (preEdit == null)
+ {
+ preEdit = new Sequence("", seq.getSequenceAsString());
+ preEdit.setDatasetSequence(ds);
+ result.put(ds, preEdit);
+ }
+ }
+ }
+ return result;
+ }
+
+ /*
+ * Work backwards through the edit list, deriving the sequences before each
+ * was applied. The final result is the sequence set before any edits.
+ */
+ Iterator<Edit> edits = new ReverseListIterator<Edit>(getEdits());
+ while (edits.hasNext())
+ {
+ Edit oldEdit = edits.next();
+ Action action = oldEdit.getAction();
+ int position = oldEdit.getPosition();
+ int number = oldEdit.getNumber();
+ final char gap = oldEdit.getGapCharacter();
+ for (SequenceI seq : oldEdit.getSequences())
+ {
+ SequenceI ds = seq.getDatasetSequence();
+ SequenceI preEdit = result.get(ds);
+ if (preEdit == null)
+ {
+ preEdit = new Sequence("", seq.getSequenceAsString());
+ preEdit.setDatasetSequence(ds);
+ result.put(ds, preEdit);
+ }
+ /*
+ * 'Undo' this edit action on the sequence (updating the value in the
+ * map).
+ */
+ if (ds != null)
+ {
+ if (action == Action.DELETE_GAP)
+ {
+ preEdit.setSequence(new String(StringUtils.insertCharAt(
+ preEdit.getSequence(), position,
+ number, gap)));
+ }
+ else if (action == Action.INSERT_GAP)
+ {
+ preEdit.setSequence(new String(StringUtils.deleteChars(
+ preEdit.getSequence(), position, position + number)));
+ }
+ else
+ {
+ throw new IllegalStateException("Can't undo edit action " + action);
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ public class Edit
{
public SequenceI[] oldds;
char gapChar;
- Edit(Action command, SequenceI[] seqs, int position, int number,
+ public Edit(Action command, SequenceI[] seqs, int position, int number,
char gapChar)
{
this.command = command;
fullAlignmentHeight = (al.getHeight() == seqs.length);
}
+
+ public SequenceI[] getSequences()
+ {
+ return seqs;
+ }
+
+ public int getPosition()
+ {
+ return position;
+ }
+
+ public Action getAction()
+ {
+ return command;
+ }
+
+ public int getNumber()
+ {
+ return number;
+ }
+
+ public char getGapCharacter()
+ {
+ return gapChar;
+ }
+ }
+
+ /**
+ * Returns an iterator over the list of edit commands which traverses the list
+ * either forwards or backwards.
+ *
+ * @param forwards
+ * @return
+ */
+ public Iterator<Edit> getEditIterator(boolean forwards)
+ {
+ if (forwards)
+ {
+ return getEdits().iterator();
+ }
+ else
+ {
+ return new ReverseListIterator<Edit>(getEdits());
+ }
}
}
this.end = end;
}
}
+
+ /**
+ * Returns true if no search result matches are held.
+ *
+ * @return
+ */
+ public boolean isEmpty()
+ {
+ return (matches == null) || (matches.length == 0);
+ }
}
package jalview.datamodel;
import jalview.analysis.AlignSeq;
+import jalview.util.StringUtils;
import java.util.ArrayList;
import java.util.Enumeration;
}
}
+ /**
+ * Returns the (1-based) residue position for a (0-based) alignment position
+ *
+ * @param i
+ * column index in alignment (from 0..<length)
+ *
+ * @return residue number for residue (left of and) nearest ith column
+ */
@Override
public int findPosition(int i)
{
return map;
}
- /*
- * (non-Javadoc)
+ /**
+ * Delete sequence characters from position 'from' (inclusive) to 'to'
+ * (exclusive). The end range is limited to the length of the sequence. If the
+ * start position is out of range, do nothing.
*
* @see jalview.datamodel.SequenceI#deleteChars(int, int)
*/
- public void deleteChars(int i, int j)
+ @Override
+ public void deleteChars(int from, int to)
{
- int newstart = start, newend = end;
- if (i >= sequence.length)
+ if (from >= sequence.length)
{
return;
}
-
- char[] tmp;
-
- if (j >= sequence.length)
- {
- tmp = new char[i];
- System.arraycopy(sequence, 0, tmp, 0, i);
- j = sequence.length;
- }
- else
+ if (to > sequence.length)
{
- tmp = new char[sequence.length - j + i];
- System.arraycopy(sequence, 0, tmp, 0, i);
- System.arraycopy(sequence, j, tmp, i, sequence.length - j);
+ to = sequence.length;
}
+ char[] tmp = StringUtils.deleteChars(sequence, from, to);
+
+ recreateDatasetAfterDeletions(from, to);
+ sequence = tmp;
+ }
+
+ /**
+ * Reconstruct the dataset sequence, if necessary, after deletion of columns
+ * in the aligned sequence.
+ *
+ * @param from
+ * start column of deletions (zero-based, inclusive)
+ * @param to
+ * end column of deletions (exclusive)
+ */
+ protected boolean recreateDatasetAfterDeletions(int from, int to)
+ {
+ boolean created = false;
boolean createNewDs = false;
+ int newstart = start, newend = end;
// TODO: take a look at the new dataset creation validation method below -
- // this could become time comsuming for large sequences - consider making it
+ // this could become time consuming for large sequences - consider making it
// more efficient
- for (int s = i; s < j; s++)
+ for (int s = from; s < to; s++)
{
if (jalview.schemes.ResidueProperties.aaIndex[sequence[s]] != 23)
{
if (sindex == s)
{
// delete characters including start of sequence
- newstart = findPosition(j);
+ newstart = findPosition(to);
break; // don't need to search for any more residue characters.
}
else
{
// delete characters after start.
int eindex = findIndex(end) - 1;
- if (eindex < j)
+ if (eindex < to)
{
// delete characters at end of sequence
- newend = findPosition(i - 1);
+ newend = findPosition(from - 1);
break; // don't need to search for any more residue characters.
}
else
Sequence ds = new Sequence(datasetSequence);
// TODO: remove any non-inheritable properties ?
// TODO: create a sequence mapping (since there is a relation here ?)
- ds.deleteChars(i, j);
+ ds.deleteChars(from, to);
datasetSequence = ds;
+ created = true;
}
start = newstart;
end = newend;
- sequence = tmp;
+ return created;
}
- /**
- * DOCUMENT ME!
- *
- * @param i
- * DOCUMENT ME!
- * @param c
- * DOCUMENT ME!
- * @param chop
- * DOCUMENT ME!
- */
- public void insertCharAt(int i, int length, char c)
+ @Override
+ public void insertCharAt(int i, char c)
{
- char[] tmp = new char[sequence.length + length];
-
- if (i >= sequence.length)
- {
- System.arraycopy(sequence, 0, tmp, 0, sequence.length);
- i = sequence.length;
- }
- else
- {
- System.arraycopy(sequence, 0, tmp, 0, i);
- }
-
- int index = i;
- while (length > 0)
- {
- tmp[index++] = c;
- length--;
- }
-
- if (i < sequence.length)
- {
- System.arraycopy(sequence, i, tmp, index, sequence.length - i);
- }
-
- sequence = tmp;
+ insertCharAt(i, 1, c);
}
- public void insertCharAt(int i, char c)
+ @Override
+ public void insertCharAt(int pos, int count, char c)
{
- insertCharAt(i, 1, c);
+ sequence = StringUtils.insertCharAt(sequence, pos, count, c);
}
public String getVamsasId()
AlignmentAnnotation _aa = new AlignmentAnnotation(aa);
_aa.sequenceRef = datasetSequence;
_aa.adjustForAlignment(); // uses annotation's own record of
- // sequence-column mapping
+ // sequence-column mapping
datasetSequence.addAlignmentAnnotation(_aa);
}
}
String label)
{
List<AlignmentAnnotation> result = new ArrayList<AlignmentAnnotation>();
- if (this.annotation != null) {
- for (AlignmentAnnotation ann : annotation) {
+ if (this.annotation != null)
+ {
+ for (AlignmentAnnotation ann : annotation)
+ {
if (ann.calcId != null && ann.calcId.equals(calcId)
&& ann.label != null && ann.label.equals(label))
{
* if necessary and adjusting start and end positions accordingly.
*
* @param i
- * first column in range to delete
+ * first column in range to delete (inclusive)
* @param j
- * last column in range to delete
+ * last column in range to delete (exclusive)
*/
public void deleteChars(int i, int j);
/**
* DOCUMENT ME!
- *
- * @param i
+ * @param position
* DOCUMENT ME!
- * @param c
+ * @param ch
* DOCUMENT ME!
*/
- public void insertCharAt(int i, int length, char c);
+ public void insertCharAt(int position, int count, char ch);
/**
* DOCUMENT ME!
import jalview.io.AppletFormatAdapter;
import jalview.schemes.ColourSchemeI;
import jalview.schemes.ResidueProperties;
+import jalview.structure.AtomSpec;
import jalview.structure.StructureListener;
import jalview.structure.StructureMapping;
import jalview.structure.StructureSelectionManager;
import java.security.AccessControlException;
import java.util.Enumeration;
import java.util.Hashtable;
+import java.util.List;
import java.util.Map;
import java.util.Vector;
+ (1 + getModelNum((String) chainFile.get(lbl))) + " or ");
}
if (cmd.length() > 0)
+ {
cmd.setLength(cmd.length() - 4);
+ }
evalStateCommand("select *;restrict " + cmd + ";cartoon;center " + cmd);
}
jalview.api.AlignmentViewPanel alignmentv)
{
if (!colourBySequence || !isLoadingFinished())
+ {
return;
+ }
if (ssm == null)
{
return;
for (jalview.structure.StructureMappingcommandSet cpdbbyseq : JmolCommands
.getColourBySequenceCommand(ssm, files, sequence, sr, fr,
alignment))
+ {
for (String cbyseq : cpdbbyseq.commands)
{
evalStateCommand(cbyseq);
}
+ }
}
public boolean isColourBySequence()
String pdbfile)
{
if (getModelNum(pdbfile) < 0)
+ {
return null;
+ }
// TODO: verify atomIndex is selecting correct model.
return new Color(viewer.getAtomArgb(atomIndex));
}
for (int i = 0; i < mfn.length; i++)
{
if (mfn[i].equalsIgnoreCase(modelFileName))
+ {
return i;
+ }
}
return -1;
}
jmolpopup.show(x, y);
}
- // jmol/ssm only
- public void highlightAtom(int atomIndex, int pdbResNum, String chain,
- String pdbfile)
+ /**
+ * Highlight the specified atoms in the structure.
+ *
+ * @param atoms
+ */
+ @Override
+ public void highlightAtoms(List<AtomSpec> atoms)
{
if (modelFileNames == null)
{
return;
}
- // look up file model number for this pdbfile
- int mdlNum = 0;
- String fn;
- // may need to adjust for URLencoding here - we don't worry about that yet.
- while (mdlNum < modelFileNames.length
- && !pdbfile.equals(modelFileNames[mdlNum]))
- {
- // System.out.println("nomatch:"+pdbfile+"\nmodelfn:"+fn);
- mdlNum++;
- }
- if (mdlNum == modelFileNames.length)
+ for (AtomSpec atom : atoms)
{
- return;
- }
+ String pdbfile = atom.getPdbId();
+ int pdbResNum = atom.getPdbResNum();
+ String chain = atom.getChain();
+
+ // look up file model number for this pdbfile
+ int mdlNum = 0;
+ String fn;
+ // may need to adjust for URLencoding here - we don't worry about that
+ // yet.
+ while (mdlNum < modelFileNames.length
+ && !pdbfile.equals(modelFileNames[mdlNum]))
+ {
+ // System.out.println("nomatch:"+pdbfile+"\nmodelfn:"+fn);
+ mdlNum++;
+ }
+ if (mdlNum == modelFileNames.length)
+ {
+ return;
+ }
- jmolHistory(false);
- // if (!pdbfile.equals(pdbentry.getFile()))
- // return;
- if (resetLastRes.length() > 0)
- {
- viewer.evalStringQuiet(resetLastRes.toString());
- }
+ jmolHistory(false);
+ // if (!pdbfile.equals(pdbentry.getFile()))
+ // return;
+ if (resetLastRes.length() > 0)
+ {
+ viewer.evalStringQuiet(resetLastRes.toString());
+ }
- eval.setLength(0);
- eval.append("select " + pdbResNum); // +modelNum
+ eval.setLength(0);
+ eval.append("select " + pdbResNum); // +modelNum
- resetLastRes.setLength(0);
- resetLastRes.append("select " + pdbResNum); // +modelNum
+ resetLastRes.setLength(0);
+ resetLastRes.append("select " + pdbResNum); // +modelNum
- eval.append(":");
- resetLastRes.append(":");
- if (!chain.equals(" "))
- {
- eval.append(chain);
- resetLastRes.append(chain);
- }
- {
- eval.append(" /" + (mdlNum + 1));
- resetLastRes.append("/" + (mdlNum + 1));
- }
- eval.append(";wireframe 100;" + eval.toString() + " and not hetero;");
+ eval.append(":");
+ resetLastRes.append(":");
+ if (!chain.equals(" "))
+ {
+ eval.append(chain);
+ resetLastRes.append(chain);
+ }
+ {
+ eval.append(" /" + (mdlNum + 1));
+ resetLastRes.append("/" + (mdlNum + 1));
+ }
+ eval.append(";wireframe 100;" + eval.toString() + " and not hetero;");
- resetLastRes.append(";wireframe 0;" + resetLastRes.toString()
- + " and not hetero; spacefill 0;");
+ resetLastRes.append(";wireframe 0;" + resetLastRes.toString()
+ + " and not hetero; spacefill 0;");
- eval.append("spacefill 200;select none");
+ eval.append("spacefill 200;select none");
- viewer.evalStringQuiet(eval.toString());
- jmolHistory(true);
+ viewer.evalStringQuiet(eval.toString());
+ jmolHistory(true);
+ }
}
String chainId;
if (strInfo.indexOf(":") > -1)
+ {
chainId = strInfo.substring(strInfo.indexOf(":") + 1,
strInfo.indexOf("."));
+ }
else
{
chainId = " ";
;
}
if (lastMessage == null || !lastMessage.equals(strInfo))
+ {
ssm.mouseOverStructure(pdbResNum, chainId, pdbfilename);
+ }
lastMessage = strInfo;
}
int chainSeparator = strInfo.indexOf(":");
int p = 0;
if (chainSeparator == -1)
+ {
chainSeparator = strInfo.indexOf(".");
+ }
String picked = strInfo.substring(strInfo.indexOf("]") + 1,
chainSeparator);
String mdlString = "";
if ((p = strInfo.indexOf(":")) > -1)
+ {
picked += strInfo.substring(p + 1, strInfo.indexOf("."));
+ }
if ((p = strInfo.indexOf("/")) > -1)
{
for (int i = 0; i < pdb.chains.size(); i++)
{
String chid = new String(pdb.id + ":"
- + ((MCview.PDBChain) pdb.chains.elementAt(i)).id);
+ + pdb.chains.elementAt(i).id);
chainFile.put(chid, fileName);
chainNames.addElement(chid);
}
colourBySequence = false;
if (cs == null)
+ {
return;
+ }
String res;
int index;
res = en.nextElement().toString();
index = ((Integer) ResidueProperties.aa3Hash.get(res)).intValue();
if (index > 20)
+ {
continue;
+ }
col = cs.findColour(ResidueProperties.aa[index].charAt(0));
import jalview.io.AppletFormatAdapter;
import jalview.schemes.ColourSchemeI;
import jalview.schemes.ResidueProperties;
+import jalview.structure.AtomSpec;
import jalview.structure.StructureListener;
import jalview.structure.StructureMapping;
import jalview.structure.StructureSelectionManager;
public abstract SequenceRenderer getSequenceRenderer(
AlignmentViewPanel alignment);
- // jmol/ssm only
- public void highlightAtom(int atomIndex, int pdbResNum, String chain,
- String pdbfile)
+ /**
+ * Highlight the specified atom positions in the structure.
+ *
+ * @param atomIndex
+ * @param pdbResNum
+ * @param chain
+ * @param pdbfile
+ */
+ @Override
+ public void highlightAtoms(List<AtomSpec> atoms)
{
- List<ChimeraModel> cms = chimmaps.get(pdbfile);
- if (cms != null)
+ for (AtomSpec atom : atoms)
{
- int mdlNum = cms.get(0).getModelNumber();
-
- viewerCommandHistory(false);
- // viewer.stopListening();
- if (resetLastRes.length() > 0)
+ int pdbResNum = atom.getPdbResNum();
+ String chain = atom.getChain();
+ List<ChimeraModel> cms = chimmaps.get(atom.getPdbId());
+ if (cms != null)
{
- eval.setLength(0);
- eval.append(resetLastRes.toString() + ";");
- }
+ int mdlNum = cms.get(0).getModelNumber();
+
+ viewerCommandHistory(false);
+ // viewer.stopListening();
+ if (resetLastRes.length() > 0)
+ {
+ eval.setLength(0);
+ eval.append(resetLastRes.toString() + ";");
+ }
- eval.append("display "); // +modelNum
+ eval.append("display "); // +modelNum
- resetLastRes.setLength(0);
- resetLastRes.append("~display ");
- {
- eval.append(" #" + (mdlNum));
- resetLastRes.append(" #" + (mdlNum));
- }
- // complete select string
+ resetLastRes.setLength(0);
+ resetLastRes.append("~display ");
+ {
+ eval.append(" #" + (mdlNum));
+ resetLastRes.append(" #" + (mdlNum));
+ }
+ // complete select string
- eval.append(":" + pdbResNum);
- resetLastRes.append(":" + pdbResNum);
- if (!chain.equals(" "))
- {
- eval.append("." + chain);
- resetLastRes.append("." + chain);
+ eval.append(":" + pdbResNum);
+ resetLastRes.append(":" + pdbResNum);
+ if (!chain.equals(" "))
+ {
+ eval.append("." + chain);
+ resetLastRes.append("." + chain);
+ }
+
+ viewer.sendChimeraCommand(eval.toString(), false);
+ viewerCommandHistory(true);
+ // viewer.startListening();
}
-
- viewer.sendChimeraCommand(eval.toString(), false);
- viewerCommandHistory(true);
- // viewer.startListening();
}
}
package jalview.ext.varna;
import java.awt.event.*;
+import java.util.List;
import jalview.api.SequenceStructureBinding;
import jalview.api.StructureSelectionManagerProvider;
{
+ @Override
+ public void highlightAtoms(List<AtomSpec> atoms)
+ {
+ }
+
}
void updateEditMenuBar()
{
- if (viewport.historyList.size() > 0)
+ if (viewport.getHistoryList().size() > 0)
{
undoMenuItem.setEnabled(true);
- CommandI command = viewport.historyList.peek();
+ CommandI command = viewport.getHistoryList().peek();
undoMenuItem.setText(MessageManager.formatMessage(
"label.undo_command", new String[]
{ command.getDescription() }));
undoMenuItem.setText(MessageManager.getString("action.undo"));
}
- if (viewport.redoList.size() > 0)
+ if (viewport.getRedoList().size() > 0)
{
redoMenuItem.setEnabled(true);
- CommandI command = viewport.redoList.peek();
+ CommandI command = viewport.getRedoList().peek();
redoMenuItem.setText(MessageManager.formatMessage(
"label.redo_command", new String[]
{ command.getDescription() }));
{
if (command.getSize() > 0)
{
- viewport.historyList.push(command);
- viewport.redoList.clear();
+ viewport.addToHistoryList(command);
+ viewport.clearRedoList();
updateEditMenuBar();
viewport.updateHiddenColumns();
// viewport.hasHiddenColumns = (viewport.getColumnSelection() != null
@Override
protected void undoMenuItem_actionPerformed(ActionEvent e)
{
- if (viewport.historyList.empty())
+ if (viewport.getHistoryList().isEmpty())
{
return;
}
- CommandI command = viewport.historyList.pop();
- viewport.redoList.push(command);
+ CommandI command = viewport.getHistoryList().pop();
+ viewport.addToRedoList(command);
+ // TODO: execute command before adding to redo list / broadcasting?
command.undoCommand(getViewAlignments());
AlignViewport originalSource = getOriginatingSource(command);
@Override
protected void redoMenuItem_actionPerformed(ActionEvent e)
{
- if (viewport.redoList.size() < 1)
+ if (viewport.getRedoList().size() < 1)
{
return;
}
- CommandI command = viewport.redoList.pop();
- viewport.historyList.push(command);
+ CommandI command = viewport.getRedoList().pop();
+ viewport.addToHistoryList(command);
command.doCommand(getViewAlignments());
AlignViewport originalSource = getOriginatingSource(command);
}
boolean appendHistoryItem = false;
- if (viewport.historyList != null && viewport.historyList.size() > 0
- && viewport.historyList.peek() instanceof SlideSequencesCommand)
+ if (viewport.getHistoryList() != null
+ && viewport.getHistoryList().size() > 0
+ && viewport.getHistoryList().peek() instanceof SlideSequencesCommand)
{
appendHistoryItem = ssc
- .appendSlideCommand((SlideSequencesCommand) viewport.historyList
+ .appendSlideCommand((SlideSequencesCommand) viewport
+ .getHistoryList()
.peek());
}
new Finder();
}
+ /**
+ * Create a new view of the current alignment.
+ */
@Override
public void newView_actionPerformed(ActionEvent e)
{
viewport.viewName = "Original";
}
- newap.av.historyList = viewport.historyList;
- newap.av.redoList = viewport.redoList;
+ newap.av.setHistoryList(viewport.getHistoryList());
+ newap.av.setRedoList(viewport.getRedoList());
int index = Desktop.getViewCount(viewport.getSequenceSetId());
// make sure the new view has a unique name - this is essential for Jalview
}
}
+ /**
+ * Construct and display a new frame containing the translation of this
+ * frame's cDNA sequences to their protein (amino acid) equivalents.
+ */
@Override
public void showTranslation_actionPerformed(ActionEvent e)
{
Desktop.addInternalFrame(af, MessageManager.formatMessage(
"label.translation_of_params", new String[]
{ this.getTitle() }), DEFAULT_WIDTH, DEFAULT_HEIGHT);
+ viewport.getStructureSelectionManager().addCommandListener(viewport);
}
}
import jalview.api.AlignViewportI;
import jalview.bin.Cache;
import jalview.commands.CommandI;
+import jalview.commands.EditCommand;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.ColumnSelection;
import jalview.datamodel.PDBEntry;
import jalview.datamodel.SequenceI;
import jalview.schemes.ColourSchemeProperty;
import jalview.schemes.UserColourScheme;
+import jalview.structure.CommandListener;
import jalview.structure.SelectionSource;
import jalview.structure.StructureSelectionManager;
import jalview.structure.VamsasSource;
import java.awt.Container;
import java.awt.Font;
import java.awt.Rectangle;
+import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Deque;
import java.util.Hashtable;
-import java.util.Stack;
+import java.util.List;
import java.util.Vector;
/**
* @version $Revision: 1.141 $
*/
public class AlignViewport extends AlignmentViewport implements
- SelectionSource, VamsasSource, AlignViewportI
+ SelectionSource, VamsasSource, AlignViewportI, CommandListener
{
int startRes;
boolean gatherViewsHere = false;
- Stack<CommandI> historyList = new Stack<CommandI>();
+ private Deque<CommandI> historyList = new ArrayDeque<CommandI>();
- Stack<CommandI> redoList = new Stack<CommandI>();
+ private Deque<CommandI> redoList = new ArrayDeque<CommandI>();
int thresholdTextColour = 0;
{
this.showAutocalculatedAbove = showAutocalculatedAbove;
}
+
+ /**
+ * Method called when another alignment's edit (or possibly other) command is
+ * broadcast to here.
+ *
+ * To allow for sequence mappings (e.g. protein to cDNA), we have to first
+ * 'unwind' the command on the source sequences (in simulation, not in fact),
+ * and then for each edit in turn:
+ * <ul>
+ * <li>compute the equivalent edit on the mapped sequences</li>
+ * <li>apply the mapped edit</li>
+ * <li>'apply' the source edit to the working copy of the source sequences</li>
+ * </ul>
+ *
+ * @param command
+ * @param undo
+ * @param ssm
+ */
+ @Override
+ public void mirrorCommand(CommandI command, boolean undo,
+ StructureSelectionManager ssm)
+ {
+ /*
+ * Only EditCommand is currently handled by listeners.
+ */
+ if (!(command instanceof EditCommand))
+ {
+ return;
+ }
+ EditCommand edit = (EditCommand) command;
+
+ List<SequenceI> seqs = getAlignment().getSequences();
+ EditCommand mappedCommand = ssm.mapEditCommand(edit, undo, seqs,
+ getGapCharacter());
+ AlignmentI[] views = getAlignPanel().alignFrame.getViewAlignments();
+ mappedCommand.performEdit(0, views);
+ getAlignPanel().alignmentChanged();
+ }
+
+ @Override
+ public VamsasSource getVamsasSource()
+ {
+ return this;
+ }
+
+ /**
+ * Add one command to the command history list.
+ *
+ * @param command
+ */
+ public void addToHistoryList(CommandI command)
+ {
+ if (this.historyList != null)
+ {
+ this.historyList.push(command);
+ broadcastCommand(command, false);
+ }
+ }
+
+ protected void broadcastCommand(CommandI command, boolean undo)
+ {
+ getStructureSelectionManager().commandPerformed(command, undo, getVamsasSource());
+ }
+
+ /**
+ * Add one command to the command redo list.
+ *
+ * @param command
+ */
+ public void addToRedoList(CommandI command)
+ {
+ if (this.redoList != null)
+ {
+ this.redoList.push(command);
+ }
+ broadcastCommand(command, true);
+ }
+
+ /**
+ * Clear the command redo list.
+ */
+ public void clearRedoList()
+ {
+ if (this.redoList != null)
+ {
+ this.redoList.clear();
+ }
+ }
+
+ public void setHistoryList(Deque<CommandI> list)
+ {
+ this.historyList = list;
+ }
+
+ public Deque<CommandI> getHistoryList()
+ {
+ return this.historyList;
+ }
+
+ public void setRedoList(Deque<CommandI> list)
+ {
+ this.redoList = list;
+ }
+
+ public Deque<CommandI> getRedoList()
+ {
+ return this.redoList;
+ }
}
.getStructureSelectionManager();
ssm.removeStructureViewerListener(seqPanel, null);
ssm.removeSelectionListener(seqPanel);
+ ssm.removeEditListener(av);
av.setAlignment(null);
av = null;
}
}
@Override
- public Color getColour(int atomIndex, int pdbResNum, String chain,
- String pdbId)
- {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
public String[] getPdbFile()
{
// TODO Auto-generated method stub
}
@Override
- public void highlightAtom(int atomIndex, int pdbResNum, String chain,
- String pdbId)
- {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void mouseOverStructure(int atomIndex, String strInfo)
- {
- // TODO Auto-generated method stub
-
- }
-
- @Override
public void releaseReferences(Object svl)
{
// TODO Auto-generated method stub
if (av != null)
{
// propagate shared settings to this new view
- af.viewport.historyList = av.historyList;
- af.viewport.redoList = av.redoList;
+ af.viewport.setHistoryList(av.getHistoryList());
+ af.viewport.setRedoList(av.getRedoList());
}
else
{
}
CommandI command = historyList.pop();
- if (ap.av.historyList.contains(command))
+ if (ap.av.getHistoryList().contains(command))
{
command.undoCommand(af.getViewAlignments());
- ap.av.historyList.remove(command);
+ ap.av.getHistoryList().remove(command);
ap.av.firePropertyChange("alignment", null, ap.av.getAlignment().getSequences());
af.updateEditMenuBar();
}
import jalview.commands.EditCommand;
import jalview.commands.EditCommand.Action;
+import jalview.commands.EditCommand.Edit;
import jalview.datamodel.ColumnSelection;
import jalview.datamodel.SearchResults;
import jalview.datamodel.Sequence;
import jalview.structure.SelectionSource;
import jalview.structure.SequenceListener;
import jalview.structure.StructureSelectionManager;
+import jalview.structure.VamsasSource;
+import jalview.util.Comparison;
import jalview.util.MessageManager;
import java.awt.BorderLayout;
return features;
}
+ /**
+ * When all of a sequence of edits are complete, put the resulting edit list
+ * on the history stack (undo list), and reset flags for editing in progress.
+ */
void endEditing()
{
- if (editCommand != null && editCommand.getSize() > 0)
+ try
+ {
+ if (editCommand != null && editCommand.getSize() > 0)
+ {
+ ap.alignFrame.addHistoryItem(editCommand);
+ av.firePropertyChange("alignment", null, av.getAlignment()
+ .getSequences());
+ }
+ } finally
{
- ap.alignFrame.addHistoryItem(editCommand);
- av.firePropertyChange("alignment", null, av.getAlignment()
- .getSequences());
+ /*
+ * Tidy up come what may...
+ */
+ startseq = -1;
+ lastres = -1;
+ editingSeqs = false;
+ groupEditing = false;
+ keyboardNo1 = null;
+ keyboardNo2 = null;
+ editCommand = null;
}
-
- startseq = -1;
- lastres = -1;
- editingSeqs = false;
- groupEditing = false;
- keyboardNo1 = null;
- keyboardNo2 = null;
- editCommand = null;
}
void setCursorRow()
String lastMessage;
- @Override
public void mouseOverSequence(SequenceI sequence, int index, int pos)
{
String tmp = sequence.hashCode() + " " + index + " " + pos;
}
@Override
+ public VamsasSource getVamsasSource()
+ {
+ return this.ap == null ? null : this.ap.av;
+ }
public void updateColours(SequenceI seq, int index)
{
System.out.println("update the seqPanel colours");
}
}
- StringBuffer message = new StringBuffer();
+ StringBuilder message = new StringBuilder(64);
if (groupEditing)
{
message.append("Edit group:");
}
else
{
- editCommand.appendEdit(Action.INSERT_GAP, groupSeqs,
- startres, startres - lastres, av.getAlignment(), true);
+ appendEdit(Action.INSERT_GAP, groupSeqs, startres, startres
+ - lastres);
}
}
else
}
else
{
- editCommand.appendEdit(Action.DELETE_GAP, groupSeqs,
- startres, lastres - startres, av.getAlignment(), true);
+ appendEdit(Action.DELETE_GAP, groupSeqs, startres, lastres
+ - startres);
}
}
}
else
{
- editCommand.appendEdit(Action.INSERT_GAP, new SequenceI[]
- { seq }, lastres, startres - lastres, av.getAlignment(), true);
+ appendEdit(Action.INSERT_GAP, new SequenceI[]
+ { seq }, lastres, startres - lastres);
}
}
else
{
for (int j = lastres; j > startres; j--)
{
- if (!jalview.util.Comparison.isGap(seq.getCharAt(startres)))
+ if (!Comparison.isGap(seq.getCharAt(startres)))
{
endEditing();
break;
int max = 0;
for (int m = startres; m < lastres; m++)
{
- if (!jalview.util.Comparison.isGap(seq.getCharAt(m)))
+ if (!Comparison.isGap(seq.getCharAt(m)))
{
break;
}
if (max > 0)
{
- editCommand.appendEdit(Action.DELETE_GAP,
- new SequenceI[]
- { seq }, startres, max, av.getAlignment(), true);
+ appendEdit(Action.DELETE_GAP, new SequenceI[]
+ { seq }, startres, max);
}
}
}
}
else
{
- editCommand.appendEdit(Action.INSERT_NUC, new SequenceI[]
- { seq }, lastres, startres - lastres, av.getAlignment(), true);
+ appendEdit(Action.INSERT_NUC, new SequenceI[]
+ { seq }, lastres, startres - lastres);
}
}
}
}
}
- editCommand.appendEdit(Action.DELETE_GAP, seq, blankColumn, 1,
- av.getAlignment(), true);
+ appendEdit(Action.DELETE_GAP, seq, blankColumn, 1);
+
+ appendEdit(Action.INSERT_GAP, seq, j, 1);
+
+ }
+
+ /**
+ * Helper method to add and perform one edit action.
+ *
+ * @param action
+ * @param seq
+ * @param pos
+ * @param count
+ */
+ protected void appendEdit(Action action, SequenceI[] seq, int pos,
+ int count)
+ {
- editCommand.appendEdit(Action.INSERT_GAP, seq, j, 1,
- av.getAlignment(), true);
+ final Edit edit = new EditCommand().new Edit(action, seq, pos, count,
+ av.getAlignment().getGapCharacter());
+ editCommand.appendEdit(edit, av.getAlignment(),
+ true, null);
}
void deleteChar(int j, SequenceI[] seq, int fixedColumn)
{
- editCommand.appendEdit(Action.DELETE_GAP, seq, j, 1,
- av.getAlignment(), true);
+ appendEdit(Action.DELETE_GAP, seq, j, 1);
- editCommand.appendEdit(Action.INSERT_GAP, seq, fixedColumn, 1,
- av.getAlignment(), true);
+ appendEdit(Action.INSERT_GAP, seq, fixedColumn, 1);
}
/**
public void end_session(boolean promptUser)
{
if (!inSession())
+ {
throw new Error(MessageManager.getString("error.jalview_no_connected_vamsas_session"));
+ }
Cache.log.info("Jalview disconnecting from the Vamsas Session.");
try
{
int i = -1;
- public void mouseOver(SequenceI seq, int index,
+ @Override
+ public void mouseOverSequence(SequenceI seq, int index,
VamsasSource source)
{
if (jv2vobj == null)
+ {
return;
+ }
if (seq != last || i != index)
{
VorbaId v = (VorbaId) jv2vobj.get(seq);
int i = -1;
- public void mouseOver(SequenceI seq, int index, VamsasSource source)
+ @Override
+ public void mouseOverSequence(SequenceI seq, int index,
+ VamsasSource source)
{
if (seq != last || i != index)
{
*/
package jalview.javascript;
-import java.awt.Color;
-import java.util.ArrayList;
-
import jalview.api.AlignmentViewPanel;
import jalview.api.FeatureRenderer;
import jalview.api.SequenceRenderer;
import jalview.bin.JalviewLite;
import jalview.datamodel.SequenceI;
import jalview.ext.jmol.JmolCommands;
+import jalview.structure.AtomSpec;
import jalview.structure.StructureListener;
import jalview.structure.StructureMapping;
import jalview.structure.StructureMappingcommandSet;
import jalview.structure.StructureSelectionManager;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Propagate events involving PDB structures associated with sequences to a
* javascript function. Generally, the javascript handler is called with a
return modelSet;
}
- @Override
public void mouseOverStructure(int atomIndex, String strInfo)
{
}
@Override
- public void highlightAtom(int atomIndex, int pdbResNum, String chain,
- String pdbId)
+ public void highlightAtoms(List<AtomSpec> atoms)
{
- String[] st = new String[0];
- try
- {
- executeJavascriptFunction(_listenerfn, st = new String[]
- { "mouseover", "" + pdbId, "" + chain, "" + (pdbResNum),
- "" + atomIndex });
- } catch (Exception ex)
+ for (AtomSpec atom : atoms)
{
- System.err.println("Couldn't execute callback with " + _listenerfn
- + " using args { " + st[0] + ", " + st[1] + ", " + st[2]
- + "," + st[3] + "\n");
- ex.printStackTrace();
-
+ try
+ {
+ executeJavascriptFunction(_listenerfn, new String[]
+ { "mouseover", "" + atom.getPdbId(), "" + atom.getChain(),
+ "" + (atom.getPdbResNum()), "" + atom.getAtomIndex() });
+ } catch (Exception ex)
+ {
+ System.err.println("Couldn't execute callback with " + _listenerfn
+ + " for atomSpec: " + atom);
+ ex.printStackTrace();
+ }
}
-
}
@Override
}
@Override
- public Color getColour(int atomIndex, int pdbResNum, String chain,
- String pdbId)
- {
- return null;
- }
-
- @Override
public AlignFrame getAlignFrame()
{
// associated with all alignframes, always.
--- /dev/null
+package jalview.structure;
+
+/**
+ * Java bean representing an atom in a PDB (or similar) structure model.
+ *
+ * @author gmcarstairs
+ *
+ */
+public class AtomSpec
+{
+ private String pdbId;
+
+ private String chain;
+
+ private int pdbResNum;
+
+ private int atomIndex;
+
+ /**
+ * Constructor
+ *
+ * @param id
+ * @param chain
+ * @param resNo
+ * @param atomNo
+ */
+ public AtomSpec(String id, String chain, int resNo, int atomNo)
+ {
+ this.pdbId = id;
+ this.chain = chain;
+ this.pdbResNum = resNo;
+ this.atomIndex = atomNo;
+ }
+
+ public String getPdbId()
+ {
+ return pdbId;
+ }
+
+ public String getChain()
+ {
+ return chain;
+ }
+
+ public int getPdbResNum()
+ {
+ return pdbResNum;
+ }
+
+ public int getAtomIndex()
+ {
+ return atomIndex;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "pdbId: " + pdbId + ", chain: " + chain + ", res: " + pdbResNum
+ + ", atom: " + atomIndex;
+ }
+}
--- /dev/null
+package jalview.structure;
+
+import jalview.commands.CommandI;
+
+/**
+ * Defines a listener for commands performed on another alignment. This is to
+ * support linked editing of two alternative representations of an alignment (in
+ * particular, cDNA and protein).
+ *
+ * @author gmcarstairs
+ *
+ */
+public interface CommandListener
+{
+ /**
+ * The listener may attempt to perform the specified command; the region acted
+ * on is determined by a callback to the StructureSelectionManager (which
+ * holds mappings between alignments).
+ *
+ * @param command
+ * @param undo
+ * @param ssm
+ */
+ public void mirrorCommand(CommandI command, boolean undo,
+ StructureSelectionManager ssm);
+
+ /**
+ * Temporary workaround to make check for source == listener work.
+ *
+ * @return
+ */
+ public VamsasSource getVamsasSource();
+}
*/
package jalview.structure;
-import jalview.datamodel.*;
+import jalview.datamodel.SearchResults;
+
public interface SequenceListener
{
- public void mouseOverSequence(SequenceI sequence, int index, int pos);
-
- public void highlightSequence(jalview.datamodel.SearchResults results);
+ public void highlightSequence(SearchResults results);
- public void updateColours(SequenceI sequence, int index);
+ // Required so we can check for (and skip if) target == source
+ public VamsasSource getVamsasSource();
}
*/
package jalview.structure;
+import java.util.List;
+
public interface StructureListener
{
/**
- *
- * @return list of structure files (unique IDs/filenames) that this listener
- * handles messages for, or null if generic listener (only used by
- * removeListener method)
+ * Returns a list of structure files (unique IDs/filenames) that this listener
+ * handles messages for, or null if generic listener (only used by
+ * removeListener method)
*/
public String[] getPdbFile();
/**
- * NOT A LISTENER METHOD! called by structure viewer when the given
- * atom/structure has been moused over. Typically, implementors call
- * StructureSelectionManager.mouseOverStructure
- *
- * @param atomIndex
- * @param strInfo
- */
- public void mouseOverStructure(int atomIndex, String strInfo);
-
- /**
- * called by StructureSelectionManager to inform viewer to highlight given
- * atomspec
+ * Called by StructureSelectionManager to inform viewer to highlight given
+ * atom positions
*
- * @param atomIndex
- * @param pdbResNum
- * @param chain
- * @param pdbId
+ * @param atoms
*/
- public void highlightAtom(int atomIndex, int pdbResNum, String chain,
- String pdbId);
+ public void highlightAtoms(List<AtomSpec> atoms);
/**
- * called by StructureSelectionManager when the colours of a sequence
+ * Called by StructureSelectionManager when the colours of a sequence
* associated with a structure have changed.
*
* @param source
public void updateColours(Object source);
/**
- * called by Jalview to get the colour for the given atomspec
- *
- * @param atomIndex
- * @param pdbResNum
- * @param chain
- * @param pdbId
- * @return
- */
- public java.awt.Color getColour(int atomIndex, int pdbResNum,
- String chain, String pdbId);
-
- /**
- * called by structureSelectionManager to instruct implementor to release any
+ * Called by structureSelectionManager to instruct implementor to release any
* direct references it may hold to the given object (typically, these are
* Jalview alignment panels).
*
import jalview.analysis.AlignSeq;
import jalview.api.StructureSelectionManagerProvider;
+import jalview.commands.CommandI;
+import jalview.commands.EditCommand;
+import jalview.commands.EditCommand.Action;
+import jalview.commands.EditCommand.Edit;
import jalview.datamodel.AlignedCodonFrame;
import jalview.datamodel.AlignmentAnnotation;
import jalview.datamodel.Annotation;
import jalview.datamodel.PDBEntry;
import jalview.datamodel.SearchResults;
+import jalview.datamodel.Sequence;
import jalview.datamodel.SequenceI;
import jalview.io.AppletFormatAdapter;
import jalview.util.MessageManager;
+import jalview.util.StringUtils;
import java.io.PrintStream;
+import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
import java.util.Vector;
import MCview.Atom;
private boolean processSecondaryStructure = false,
secStructServices = false, addTempFacAnnot = false;
+ List<AlignedCodonFrame> seqmappings = null;
+
+ private int[] seqmappingrefs = null; // refcount for seqmappings elements
+
+ private List<CommandListener> commandListeners = new ArrayList<CommandListener>();
+
/**
* @return true if will try to use external services for processing secondary
* structure
// construct highlighted sequence list
if (seqmappings != null)
{
-
- Enumeration e = seqmappings.elements();
- while (e.hasMoreElements())
-
+ for (AlignedCodonFrame acf : seqmappings)
{
- ((AlignedCodonFrame) e.nextElement()).markMappedRegion(
- mappings[j].sequence, indexpos, results);
+ acf.markMappedRegion(mappings[j].sequence, indexpos,
+ results);
}
}
}
-
}
}
}
}
}
- Vector seqmappings = null; // should be a simpler list of mapped seuqence
-
/**
* highlight regions associated with a position (indexpos) in seq
*
* @param seq
- * the sequeence that the mouse over occured on
+ * the sequence that the mouse over occurred on
* @param indexpos
* the absolute position being mouseovered in seq (0 to seq.length())
* @param index
{
index = seq.findPosition(indexpos);
}
- StructureListener sl;
- int atomNo = 0;
for (int i = 0; i < listeners.size(); i++)
{
Object listener = listeners.elementAt(i);
if (listener == source)
{
+ // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
+ // Temporary fudge with SequenceListener.getVamsasSource()
continue;
}
if (listener instanceof StructureListener)
{
- sl = (StructureListener) listener;
- if (mappings == null)
- {
- continue;
- }
- for (int j = 0; j < mappings.length; j++)
- {
- if (mappings[j].sequence == seq
- || mappings[j].sequence == seq.getDatasetSequence())
- {
- atomNo = mappings[j].getAtomNum(index);
-
- if (atomNo > 0)
- {
- sl.highlightAtom(atomNo, mappings[j].getPDBResNum(index),
- mappings[j].pdbchain, mappings[j].pdbfile);
- }
- }
- }
+ highlightStructure((StructureListener) listener, seq, index);
}
else
{
- if (relaySeqMappings && hasSequenceListeners
- && listener instanceof SequenceListener)
+ if (listener instanceof SequenceListener)
{
- // DEBUG
- // System.err.println("relay Seq " + seq.getDisplayId(false) + " " +
- // index);
-
- if (results == null)
+ final SequenceListener seqListener = (SequenceListener) listener;
+ if (hasSequenceListeners
+ && seqListener.getVamsasSource() != source)
{
- results = new SearchResults();
- if (index >= seq.getStart() && index <= seq.getEnd())
+ if (relaySeqMappings)
{
- // construct highlighted sequence list
-
- if (seqmappings != null)
- {
- Enumeration e = seqmappings.elements();
- while (e.hasMoreElements())
-
- {
- ((AlignedCodonFrame) e.nextElement()).markMappedRegion(
- seq, index, results);
- }
- }
- // hasSequenceListeners = results.getSize() > 0;
- if (handlingVamsasMo)
+ if (results == null)
{
- // maybe have to resolve seq to a dataset seqeunce...
- // add in additional direct sequence and/or dataset sequence
- // highlighting
- results.addResult(seq, index, index);
+ results = buildSearchResults(seq, index);
}
+ seqListener.highlightSequence(results);
}
}
- if (hasSequenceListeners)
- {
- ((SequenceListener) listener).highlightSequence(results);
- }
}
else if (listener instanceof VamsasListener && !handlingVamsasMo)
{
- // DEBUG
- // System.err.println("Vamsas from Seq " + seq.getDisplayId(false) + "
- // " +
- // index);
- // pass the mouse over and absolute position onto the
- // VamsasListener(s)
- ((VamsasListener) listener).mouseOver(seq, indexpos, source);
+ ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
+ source);
}
else if (listener instanceof SecondaryStructureListener)
{
}
/**
+ * Returns a SearchResults object describing the mapped region corresponding
+ * to the specified sequence position.
+ *
+ * @param seq
+ * @param index
+ * @return
+ */
+ protected SearchResults buildSearchResults(SequenceI seq, int index)
+ {
+ SearchResults results;
+ results = new SearchResults();
+ if (index >= seq.getStart() && index <= seq.getEnd())
+ {
+ if (seqmappings != null)
+ {
+ for (AlignedCodonFrame acf : seqmappings)
+ {
+ acf.markMappedRegion(seq, index, results);
+ }
+ }
+ // hasSequenceListeners = results.getSize() > 0;
+ if (handlingVamsasMo)
+ {
+ // maybe have to resolve seq to a dataset sequence...
+ // add in additional direct sequence and/or dataset sequence
+ // highlighting
+ results.addResult(seq, index, index);
+ }
+ }
+ return results;
+ }
+
+ /**
+ * Send suitable messages to a StructureListener to highlight atoms
+ * corresponding to the given sequence position.
+ *
+ * @param sl
+ * @param seq
+ * @param index
+ */
+ protected void highlightStructure(StructureListener sl, SequenceI seq,
+ int index)
+ {
+ int atomNo;
+ if (mappings != null)
+ {
+ List<AtomSpec> atoms = new ArrayList<AtomSpec>();
+ for (int j = 0; j < mappings.length; j++)
+ {
+ if (mappings[j].sequence == seq
+ || mappings[j].sequence == seq.getDatasetSequence())
+ {
+ atomNo = mappings[j].getAtomNum(index);
+
+ if (atomNo > 0)
+ {
+ atoms.add(new AtomSpec(mappings[j].pdbfile,
+ mappings[j].pdbchain, mappings[j].getPDBResNum(index),
+ atomNo));
+ }
+ }
+ }
+ sl.highlightAtoms(atoms);
+ }
+ }
+
+ /**
* true if a mouse over event from an external (ie Vamsas) source is being
* handled
*/
return sb.toString();
}
- private int[] seqmappingrefs = null; // refcount for seqmappings elements
-
private synchronized void modifySeqMappingList(boolean add,
AlignedCodonFrame[] codonFrames)
{
}
if (seqmappings == null)
{
- seqmappings = new Vector();
+ seqmappings = new ArrayList<AlignedCodonFrame>();
}
if (codonFrames != null && codonFrames.length > 0)
{
{
if (add)
{
- seqmappings.addElement(codonFrames[cf]);
+ seqmappings.add(codonFrames[cf]);
int[] nsr = new int[(seqmappingrefs == null) ? 1
: seqmappingrefs.length + 1];
}
}
+ public void addCommandListener(CommandListener cl)
+ {
+ if (!commandListeners.contains(cl))
+ {
+ commandListeners.add(cl);
+ }
+ }
+
+ public boolean hasCommandListener(CommandListener cl)
+ {
+ return this.commandListeners.contains(cl);
+ }
+
+ public boolean removeEditListener(CommandListener l)
+ {
+ return commandListeners.remove(l);
+ }
+
+ /**
+ * Forward a command to any command listeners (except for the command's
+ * source).
+ *
+ * @param command
+ * the command to be broadcast (in its form after being performed)
+ * @param undo
+ * if true, the command was being 'undone'
+ * @param source
+ */
+ public void commandPerformed(CommandI command, boolean undo,
+ VamsasSource source)
+ {
+ for (CommandListener listener : commandListeners)
+ {
+ if (listener.getVamsasSource() != source)
+ {
+ listener.mirrorCommand(command, undo, this);
+ }
+ }
+ }
+
+ /**
+ * Returns a new EditCommand representing the given command as mapped to the
+ * given sequences. If there is no mapping, returns an empty EditCommand.
+ *
+ * @param command
+ * @param undo
+ * @param targetSeqs
+ * @param gapChar
+ * @return
+ */
+ public EditCommand mapEditCommand(EditCommand command, boolean undo,
+ final List<SequenceI> targetSeqs, char gapChar)
+ {
+ /*
+ * Cache a copy of the target sequences so we can mimic successive edits on
+ * them. This lets us compute mappings for all edits in the set.
+ */
+ Map<SequenceI, SequenceI> targetCopies = new HashMap<SequenceI, SequenceI>();
+ for (SequenceI seq : targetSeqs)
+ {
+ SequenceI ds = seq.getDatasetSequence();
+ if (ds != null)
+ {
+ final Sequence copy = new Sequence("", new String(seq.getSequence()));
+ copy.setDatasetSequence(ds);
+ targetCopies.put(ds, copy);
+ }
+ }
+
+ /*
+ * Compute 'source' sequences as they were before applying edits:
+ */
+ Map<SequenceI, SequenceI> originalSequences = command.priorState(undo);
+
+ EditCommand result = new EditCommand();
+ Iterator<Edit> edits = command.getEditIterator(!undo);
+ while (edits.hasNext())
+ {
+ Edit edit = edits.next();
+ Action action = edit.getAction();
+
+ /*
+ * Invert sense of action if an Undo.
+ */
+ if (undo)
+ {
+ action = action == Action.INSERT_GAP ? Action.DELETE_GAP
+ : (action == Action.DELETE_GAP ? Action.INSERT_GAP : action);
+ }
+ final int count = edit.getNumber();
+ final int editPos = edit.getPosition();
+ for (SequenceI seq : edit.getSequences())
+ {
+ /*
+ * Get residue position at (or to right of) edit location. Note we use
+ * our 'copy' of the sequence before editing for this.
+ */
+ SequenceI ds = seq.getDatasetSequence();
+ if (ds == null)
+ {
+ continue;
+ }
+ final SequenceI actedOn = originalSequences.get(ds);
+ final int seqpos = actedOn.findPosition(editPos);
+
+ /*
+ * Determine all mappings from this position to mapped sequences.
+ */
+ SearchResults sr = buildSearchResults(seq, seqpos);
+
+ if (!sr.isEmpty())
+ {
+ for (SequenceI targetSeq : targetSeqs)
+ {
+ ds = targetSeq.getDatasetSequence();
+ if (ds == null)
+ {
+ continue;
+ }
+ SequenceI copyTarget = targetCopies.get(ds);
+ final int[] match = sr.getResults(copyTarget, 0,
+ copyTarget.getLength());
+ if (match != null)
+ {
+ final int ratio = 3; // TODO: compute this - how?
+ final int mappedCount = count * ratio;
+
+ /*
+ * Shift Delete start position left, as it acts on positions to
+ * its right.
+ */
+ int mappedEditPos = action == Action.DELETE_GAP ? match[0]
+ - mappedCount : match[0];
+ Edit e = new EditCommand().new Edit(action, new SequenceI[]
+ { targetSeq }, mappedEditPos, mappedCount, gapChar);
+ result.addEdit(e);
+
+ /*
+ * and 'apply' the edit to our copy of its target sequence
+ */
+ if (action == Action.INSERT_GAP)
+ {
+ copyTarget.setSequence(new String(StringUtils.insertCharAt(
+ copyTarget.getSequence(), mappedEditPos,
+ mappedCount,
+ gapChar)));
+ }
+ else if (action == Action.DELETE_GAP)
+ {
+ copyTarget.setSequence(new String(StringUtils.deleteChars(
+ copyTarget.getSequence(), mappedEditPos,
+ mappedEditPos + mappedCount)));
+ }
+ }
+ }
+ }
+ /*
+ * and 'apply' the edit to our copy of its source sequence
+ */
+ if (action == Action.INSERT_GAP) {
+ actedOn.setSequence(new String(StringUtils.insertCharAt(
+ actedOn.getSequence(), editPos, count, gapChar)));
+ }
+ else if (action == Action.DELETE_GAP)
+ {
+ actedOn.setSequence(new String(StringUtils.deleteChars(
+ actedOn.getSequence(), editPos, editPos + count)));
+ }
+ }
+ }
+ return result;
+ }
}
*/
public interface VamsasListener
{
- public void mouseOver(SequenceI seq, int index, VamsasSource source);
+ public void mouseOverSequence(SequenceI seq, int index,
+ VamsasSource source);
}
*/
package jalview.util;
-import jalview.datamodel.*;
+import jalview.datamodel.SequenceI;
/**
- * DOCUMENT ME!
- *
- * @author $author$
- * @version $Revision$
+ * Assorted methods for analysing or comparing sequences.
*/
public class Comparison
{
- /** DOCUMENT ME!! */
+ private static final int EIGHTY_FIVE = 85;
+
+ private static final int TO_UPPER_CASE = 'a' - 'A';
+
public static final String GapChars = " .-";
/**
int ilen = si.length() - 1;
int jlen = sj.length() - 1;
- while (jalview.util.Comparison.isGap(si.charAt(start + ilen)))
+ while (Comparison.isGap(si.charAt(start + ilen)))
{
ilen--;
}
- while (jalview.util.Comparison.isGap(sj.charAt(start + jlen)))
+ while (Comparison.isGap(sj.charAt(start + jlen)))
{
jlen--;
}
}
/**
- * DOCUMENT ME!
+ * Answers true if the supplied character is a recognised gap character, else
+ * false. Currently hard-coded to recognise '-', '-' or ' ' (hyphen / dot /
+ * space).
*
* @param c
- * DOCUMENT ME!
*
- * @return DOCUMENT ME!
+ * @return
*/
public static final boolean isGap(char c)
{
return (c == '-' || c == '.' || c == ' ') ? true : false;
}
+ /**
+ * Answers true if more than 85% of the sequence residues (ignoring gaps) are
+ * A, G, C, T or U, else false. This is just a heuristic guess and may give a
+ * wrong answer (as AGCT are also animo acid codes).
+ *
+ * @param seqs
+ * @return
+ */
public static final boolean isNucleotide(SequenceI[] seqs)
{
- int i = 0, iSize = seqs.length, j, jSize;
- float nt = 0, aa = 0;
- char c;
- while (i < iSize)
+ if (seqs == null)
+ {
+ return false;
+ }
+ int ntCount = 0;
+ int aaCount = 0;
+ for (SequenceI seq : seqs)
{
- jSize = seqs[i].getLength();
- for (j = 0; j < jSize; j++)
+ for (char c : seq.getSequence())
{
- c = seqs[i].getCharAt(j);
if ('a' <= c && c <= 'z')
{
- c -= ('a' - 'A');
+ c -= TO_UPPER_CASE;
}
if (c == 'A' || c == 'G' || c == 'C' || c == 'T' || c == 'U')
{
- nt++;
+ ntCount++;
}
- else if (!jalview.util.Comparison.isGap(seqs[i].getCharAt(j)))
+ else if (!Comparison.isGap(c))
{
- aa++;
+ aaCount++;
}
}
- i++;
}
- if ((nt / (nt + aa)) > 0.85f)
+ /*
+ * Check for nucleotide count > 85% of total count (in a form that evades
+ * int / float conversion or divide by zero).
+ */
+ if (ntCount * 100 > EIGHTY_FIVE * (ntCount + aaCount))
{
return true;
}
--- /dev/null
+package jalview.util;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * An iterator that traverses a list backwards.
+ *
+ * @author gmcarstairs (and checked against
+ * org.codehaus.groovey.runtime.ReverseListIterator)
+ *
+ * @param <E>
+ */
+public class ReverseListIterator<E> implements Iterator<E>
+{
+
+ private ListIterator<E> iterator;
+
+ public ReverseListIterator(List<E> stuff)
+ {
+ this.iterator = stuff.listIterator(stuff.size());
+ }
+ @Override
+ public boolean hasNext()
+ {
+ return iterator.hasPrevious();
+ }
+
+ @Override
+ public E next()
+ {
+ return iterator.previous();
+ }
+
+ @Override
+ public void remove()
+ {
+ iterator.remove();
+ }
+
+}
--- /dev/null
+package jalview.util;
+
+public class StringUtils
+{
+
+ /**
+ * Returns a new character array, after inserting characters into the given
+ * character array.
+ *
+ * @param in
+ * the character array to insert into
+ * @param position
+ * the 0-based position for insertion
+ * @param count
+ * the number of characters to insert
+ * @param ch
+ * the character to insert
+ */
+ public static final char[] insertCharAt(char[] in, int position,
+ int count,
+ char ch)
+ {
+ char[] tmp = new char[in.length + count];
+
+ if (position >= in.length)
+ {
+ System.arraycopy(in, 0, tmp, 0, in.length);
+ position = in.length;
+ }
+ else
+ {
+ System.arraycopy(in, 0, tmp, 0, position);
+ }
+
+ int index = position;
+ while (count > 0)
+ {
+ tmp[index++] = ch;
+ count--;
+ }
+
+ if (position < in.length)
+ {
+ System.arraycopy(in, position, tmp, index,
+ in.length - position);
+ }
+
+ return tmp;
+ }
+
+ /**
+ * Delete
+ *
+ * @param in
+ * @param from
+ * @param to
+ * @return
+ */
+ public static final char[] deleteChars(char[] in, int from, int to)
+ {
+ if (from >= in.length)
+ {
+ return in;
+ }
+
+ char[] tmp;
+
+ if (to >= in.length)
+ {
+ tmp = new char[from];
+ System.arraycopy(in, 0, tmp, 0, from);
+ to = in.length;
+ }
+ else
+ {
+ tmp = new char[in.length - to + from];
+ System.arraycopy(in, 0, tmp, 0, from);
+ System.arraycopy(in, to, tmp, from, in.length - to);
+ }
+ return tmp;
+ }
+}
package jalview.commands;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
import jalview.commands.EditCommand.Action;
import jalview.commands.EditCommand.Edit;
import jalview.datamodel.Alignment;
import jalview.datamodel.Sequence;
import jalview.datamodel.SequenceI;
+import java.util.Map;
+
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
testee = new EditCommand();
seqs = new SequenceI[4];
seqs[0] = new Sequence("seq0", "abcdefghjk");
+ seqs[0].setDatasetSequence(new Sequence("seq0ds", "abcdefghjk"));
seqs[1] = new Sequence("seq1", "fghjklmnopq");
+ seqs[1].setDatasetSequence(new Sequence("seq1ds", "fghjklmnopq"));
seqs[2] = new Sequence("seq2", "qrstuvwxyz");
+ seqs[2].setDatasetSequence(new Sequence("seq2ds", "qrstuvwxyz"));
seqs[3] = new Sequence("seq3", "1234567890");
+ seqs[3].setDatasetSequence(new Sequence("seq3ds", "1234567890"));
al = new Alignment(seqs);
al.setGapCharacter('?');
}
assertEquals("qrstuvwxyz", seqs[2].getSequenceAsString());
assertEquals("1234567890", seqs[3].getSequenceAsString());
seqs[1] = new Sequence("seq1", "fghjZXYnopq");
+ }
+
+ /**
+ * Test that the addEdit command correctly merges insert gap commands when
+ * possible.
+ */
+ @Test
+ public void testAddEdit_multipleInsertGap()
+ {
+ /*
+ * 3 insert gap in a row (aka mouse drag right):
+ */
+ Edit e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[]
+ { seqs[0] }, 1, 1, al);
+ testee.addEdit(e);
+ SequenceI edited = new Sequence("seq0", "a?bcdefghjk");
+ edited.setDatasetSequence(seqs[0].getDatasetSequence());
+ e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[]
+ { edited }, 2, 1, al);
+ testee.addEdit(e);
+ edited = new Sequence("seq0", "a??bcdefghjk");
+ edited.setDatasetSequence(seqs[0].getDatasetSequence());
+ e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[]
+ { edited }, 3, 1, al);
+ testee.addEdit(e);
+ assertEquals(1, testee.getSize());
+ assertEquals(Action.INSERT_GAP, testee.getEdit(0).getAction());
+ assertEquals(1, testee.getEdit(0).getPosition());
+ assertEquals(3, testee.getEdit(0).getNumber());
+
+ /*
+ * Add a non-contiguous edit - should not be merged.
+ */
+ e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[]
+ { edited }, 5, 2, al);
+ testee.addEdit(e);
+ assertEquals(2, testee.getSize());
+ assertEquals(5, testee.getEdit(1).getPosition());
+ assertEquals(2, testee.getEdit(1).getNumber());
+
+ /*
+ * Add a Delete after the Insert - should not be merged.
+ */
+ e = new EditCommand().new Edit(Action.DELETE_GAP, new SequenceI[]
+ { edited }, 6, 2, al);
+ testee.addEdit(e);
+ assertEquals(3, testee.getSize());
+ assertEquals(Action.DELETE_GAP, testee.getEdit(2).getAction());
+ assertEquals(6, testee.getEdit(2).getPosition());
+ assertEquals(2, testee.getEdit(2).getNumber());
+ }
+
+ /**
+ * Test that the addEdit command correctly merges delete gap commands when
+ * possible.
+ */
+ @Test
+ public void testAddEdit_multipleDeleteGap()
+ {
+ /*
+ * 3 delete gap in a row (aka mouse drag left):
+ */
+ seqs[0].setSequence("a???bcdefghjk");
+ Edit e = new EditCommand().new Edit(Action.DELETE_GAP, new SequenceI[]
+ { seqs[0] }, 4, 1, al);
+ testee.addEdit(e);
+ assertEquals(1, testee.getSize());
+
+ SequenceI edited = new Sequence("seq0", "a??bcdefghjk");
+ edited.setDatasetSequence(seqs[0].getDatasetSequence());
+ e = new EditCommand().new Edit(Action.DELETE_GAP, new SequenceI[]
+ { edited }, 3, 1, al);
+ testee.addEdit(e);
+ assertEquals(1, testee.getSize());
+
+ edited = new Sequence("seq0", "a?bcdefghjk");
+ edited.setDatasetSequence(seqs[0].getDatasetSequence());
+ e = new EditCommand().new Edit(Action.DELETE_GAP, new SequenceI[]
+ { edited }, 2, 1, al);
+ testee.addEdit(e);
+ assertEquals(1, testee.getSize());
+ assertEquals(Action.DELETE_GAP, testee.getEdit(0).getAction());
+ assertEquals(2, testee.getEdit(0).getPosition());
+ assertEquals(3, testee.getEdit(0).getNumber());
+
+ /*
+ * Add a non-contiguous edit - should not be merged.
+ */
+ e = new EditCommand().new Edit(Action.DELETE_GAP, new SequenceI[]
+ { edited }, 2, 1, al);
+ testee.addEdit(e);
+ assertEquals(2, testee.getSize());
+ assertEquals(Action.DELETE_GAP, testee.getEdit(0).getAction());
+ assertEquals(2, testee.getEdit(1).getPosition());
+ assertEquals(1, testee.getEdit(1).getNumber());
+
+ /*
+ * Add an Insert after the Delete - should not be merged.
+ */
+ e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[]
+ { edited }, 1, 1, al);
+ testee.addEdit(e);
+ assertEquals(3, testee.getSize());
+ assertEquals(Action.INSERT_GAP, testee.getEdit(2).getAction());
+ assertEquals(1, testee.getEdit(2).getPosition());
+ assertEquals(1, testee.getEdit(2).getNumber());
+ }
+
+ /**
+ * Test that the addEdit command correctly handles 'remove gaps' edits for the
+ * case when they appear contiguous but are acting on different sequences.
+ * They should not be merged.
+ */
+ @Test
+ public void testAddEdit_removeAllGaps()
+ {
+ seqs[0].setSequence("a???bcdefghjk");
+ Edit e = new EditCommand().new Edit(Action.DELETE_GAP, new SequenceI[]
+ { seqs[0] }, 4, 1, al);
+ testee.addEdit(e);
+
+ seqs[1].setSequence("f??ghjklmnopq");
+ Edit e2 = new EditCommand().new Edit(Action.DELETE_GAP, new SequenceI[]
+ { seqs[1] }, 3, 1, al);
+ testee.addEdit(e2);
+ assertEquals(2, testee.getSize());
+ assertSame(e, testee.getEdit(0));
+ assertSame(e2, testee.getEdit(1));
+ }
+
+ /**
+ * Test that the addEdit command correctly merges insert gap commands acting
+ * on a multi-sequence selection.
+ */
+ @Test
+ public void testAddEdit_groupInsertGaps()
+ {
+ /*
+ * 2 insert gap in a row (aka mouse drag right), on two sequences:
+ */
+ Edit e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[]
+ { seqs[0], seqs[1] }, 1, 1, al);
+ testee.addEdit(e);
+ SequenceI seq1edited = new Sequence("seq0", "a?bcdefghjk");
+ seq1edited.setDatasetSequence(seqs[0].getDatasetSequence());
+ SequenceI seq2edited = new Sequence("seq1", "f?ghjklmnopq");
+ seq2edited.setDatasetSequence(seqs[1].getDatasetSequence());
+ e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[]
+ { seq1edited, seq2edited }, 2, 1, al);
+ testee.addEdit(e);
+
+ assertEquals(1, testee.getSize());
+ assertEquals(Action.INSERT_GAP, testee.getEdit(0).getAction());
+ assertEquals(1, testee.getEdit(0).getPosition());
+ assertEquals(2, testee.getEdit(0).getNumber());
+ assertEquals(seqs[0].getDatasetSequence(), testee.getEdit(0)
+ .getSequences()[0].getDatasetSequence());
+ assertEquals(seqs[1].getDatasetSequence(), testee.getEdit(0)
+ .getSequences()[1].getDatasetSequence());
+ }
+
+ /**
+ * Test for 'undoing' a series of gap insertions.
+ * <ul>
+ * <li>Start: ABCDEF insert 2 at pos 1</li>
+ * <li>next: A--BCDEF insert 1 at pos 4</li>
+ * <li>next: A--B-CDEF insert 2 at pos 0</li>
+ * <li>last: --A--B-CDEF</li>
+ * </ul>
+ */
+ @Test
+ public void testUnwindCommand_multipleInserts()
+ {
+ EditCommand command = new EditCommand();
+ SequenceI seq = new Sequence("", "--A--B-CDEF");
+ SequenceI ds = new Sequence("", "ABCDEF");
+ seq.setDatasetSequence(ds);
+ SequenceI[] seqs = new SequenceI[]
+ { seq };
+ Edit e = command.new Edit(Action.INSERT_GAP, seqs, 1, 2, '-');
+ command.addEdit(e);
+ e = command.new Edit(Action.INSERT_GAP, seqs, 4, 1, '-');
+ command.addEdit(e);
+ e = command.new Edit(Action.INSERT_GAP, seqs, 0, 2, '-');
+ command.addEdit(e);
+
+ Map<SequenceI, SequenceI> unwound = command.priorState(false);
+ assertEquals("ABCDEF", unwound.get(ds).getSequenceAsString());
+ }
+
+ /**
+ * Test for 'undoing' a series of gap deletions.
+ * <ul>
+ * <li>Start: A-B-C delete 1 at pos 1</li>
+ * <li>Next: AB-C delete 1 at pos 2</li>
+ * <li>End: ABC</li>
+ * </ul>
+ */
+ @Test
+ public void testUnwindCommand_removeAllGaps()
+ {
+ EditCommand command = new EditCommand();
+ SequenceI seq = new Sequence("", "ABC");
+ SequenceI ds = new Sequence("", "ABC");
+ seq.setDatasetSequence(ds);
+ SequenceI[] seqs = new SequenceI[]
+ { seq };
+ Edit e = command.new Edit(Action.DELETE_GAP, seqs, 1, 1, '-');
+ command.addEdit(e);
+ e = command.new Edit(Action.DELETE_GAP, seqs, 2, 1, '-');
+ command.addEdit(e);
+
+ Map<SequenceI, SequenceI> unwound = command.priorState(false);
+ assertEquals("A-B-C", unwound.get(ds).getSequenceAsString());
+ }
+
+ /**
+ * Test for 'undoing' a single delete edit.
+ */
+ @Test
+ public void testUnwindCommand_singleDelete()
+ {
+ EditCommand command = new EditCommand();
+ SequenceI seq = new Sequence("", "ABCDEF");
+ SequenceI ds = new Sequence("", "ABCDEF");
+ seq.setDatasetSequence(ds);
+ SequenceI[] seqs = new SequenceI[]
+ { seq };
+ Edit e = command.new Edit(Action.DELETE_GAP, seqs, 2, 2, '-');
+ command.addEdit(e);
+
+ Map<SequenceI, SequenceI> unwound = command.priorState(false);
+ assertEquals("AB--CDEF", unwound.get(ds).getSequenceAsString());
+ }
+
+ /**
+ * Test method that 'restores' edit commands to hold the sequence as it was
+ * before the edit was applied.
+ */
+ @Test
+ public void testUnwindCommand_singleInsert()
+ {
+ EditCommand command = new EditCommand();
+ SequenceI seq = new Sequence("", "AB---CDEF");
+ SequenceI ds = new Sequence("", "ABCDEF");
+ seq.setDatasetSequence(ds);
+ SequenceI[] seqs = new SequenceI[]
+ { seq };
+ Edit e = command.new Edit(Action.INSERT_GAP, seqs, 2, 3, '-');
+ command.addEdit(e);
+
+ Map<SequenceI, SequenceI> unwound = command.priorState(false);
+ assertEquals("ABCDEF", unwound.get(ds).getSequenceAsString());
+ }
+
+ /**
+ * Test that mimics 'remove all gaps' action. This generates delete gap edits
+ * for contiguous gaps in each sequence separately.
+ */
+ @Test
+ public void testUnwindCommand_removeGapsMultipleSeqs()
+ {
+ EditCommand command = new EditCommand();
+ String original1 = "--ABC-DEF";
+ String original2 = "FG-HI--J";
+ String original3 = "M-NOPQ";
+
+ /*
+ * Two edits for the first sequence
+ */
+ SequenceI seq = new Sequence("", "ABC-DEF");
+ SequenceI ds1 = new Sequence("", "ABCDEF");
+ seq.setDatasetSequence(ds1);
+ SequenceI[] seqs = new SequenceI[]
+ { seq };
+ Edit e = command.new Edit(Action.DELETE_GAP, seqs, 0, 2, '-');
+ command.addEdit(e);
+ seq = new Sequence("", "ABCDEF");
+ seq.setDatasetSequence(ds1);
+ seqs = new SequenceI[]
+ { seq };
+ e = command.new Edit(Action.DELETE_GAP, seqs, 3, 1, '-');
+ command.addEdit(e);
+
+ /*
+ * Two edits for the second sequence
+ */
+ seq = new Sequence("", "FGHI--J");
+ SequenceI ds2 = new Sequence("", "FGHIJ");
+ seq.setDatasetSequence(ds2);
+ seqs = new SequenceI[]
+ { seq };
+ e = command.new Edit(Action.DELETE_GAP, seqs, 2, 1, '-');
+ command.addEdit(e);
+ seq = new Sequence("", "FGHIJ");
+ seq.setDatasetSequence(ds2);
+ seqs = new SequenceI[]
+ { seq };
+ e = command.new Edit(Action.DELETE_GAP, seqs, 4, 2, '-');
+ command.addEdit(e);
+
+ /*
+ * One edit for the third sequence.
+ */
+ seq = new Sequence("", "MNOPQ");
+ SequenceI ds3 = new Sequence("", "MNOPQ");
+ seq.setDatasetSequence(ds3);
+ seqs = new SequenceI[]
+ { seq };
+ e = command.new Edit(Action.DELETE_GAP, seqs, 1, 1, '-');
+ command.addEdit(e);
+
+ Map<SequenceI, SequenceI> unwound = command.priorState(false);
+ assertEquals(original1, unwound.get(ds1).getSequenceAsString());
+ assertEquals(original2, unwound.get(ds2).getSequenceAsString());
+ assertEquals(original3, unwound.get(ds3).getSequenceAsString());
+ }
+
+ /**
+ * Test that mimics 'remove all gapped columns' action. This generates a
+ * series Delete Gap edits that each act on all sequences that share a gapped
+ * column region.
+ */
+ @Test
+ public void testUnwindCommand_removeGappedCols()
+ {
+ EditCommand command = new EditCommand();
+ String original1 = "--ABC--DEF";
+ String original2 = "-G-HI--J";
+ String original3 = "-M-NO--PQ";
+
+ /*
+ * First edit deletes the first column.
+ */
+ SequenceI seq1 = new Sequence("", "-ABC--DEF");
+ SequenceI ds1 = new Sequence("", "ABCDEF");
+ seq1.setDatasetSequence(ds1);
+ SequenceI seq2 = new Sequence("", "G-HI--J");
+ SequenceI ds2 = new Sequence("", "GHIJ");
+ seq2.setDatasetSequence(ds2);
+ SequenceI seq3 = new Sequence("", "M-NO--PQ");
+ SequenceI ds3 = new Sequence("", "MNOPQ");
+ seq3.setDatasetSequence(ds3);
+ SequenceI[] seqs = new SequenceI[]
+ { seq1, seq2, seq3 };
+ Edit e = command.new Edit(Action.DELETE_GAP, seqs, 0, 1, '-');
+ command.addEdit(e);
+
+ /*
+ * Second edit deletes what is now columns 4 and 5.
+ */
+ seq1 = new Sequence("", "-ABCDEF");
+ seq1.setDatasetSequence(ds1);
+ seq2 = new Sequence("", "G-HIJ");
+ seq2.setDatasetSequence(ds2);
+ seq3 = new Sequence("", "M-NOPQ");
+ seq3.setDatasetSequence(ds3);
+ seqs = new SequenceI[]
+ { seq1, seq2, seq3 };
+ e = command.new Edit(Action.DELETE_GAP, seqs, 4, 2, '-');
+ command.addEdit(e);
+ Map<SequenceI, SequenceI> unwound = command.priorState(false);
+ assertEquals(original1, unwound.get(ds1).getSequenceAsString());
+ assertEquals(original2, unwound.get(ds2).getSequenceAsString());
+ assertEquals(original3, unwound.get(ds3).getSequenceAsString());
+ assertEquals(ds1, unwound.get(ds1).getDatasetSequence());
+ assertEquals(ds2, unwound.get(ds2).getDatasetSequence());
+ assertEquals(ds3, unwound.get(ds3).getDatasetSequence());
}
}
assertSame(annotation2, anns[1]);
}
+
+ @Test
+ public void testGetStartGetEnd()
+ {
+ SequenceI seq = new Sequence("test", "ABCDEF");
+ assertEquals(1, seq.getStart());
+ assertEquals(6, seq.getEnd());
+
+ seq = new Sequence("test", "--AB-C-DEF--");
+ assertEquals(1, seq.getStart());
+ assertEquals(6, seq.getEnd());
+
+ seq = new Sequence("test", "----");
+ assertEquals(1, seq.getStart());
+ assertEquals(0, seq.getEnd()); // ??
+ }
+
+ @Test
+ public void testFindPosition()
+ {
+ SequenceI seq = new Sequence("test", "ABCDEF");
+ assertEquals(1, seq.findPosition(0));
+ assertEquals(6, seq.findPosition(5));
+ // assertEquals(-1, seq.findPosition(6)); // fails
+
+ seq = new Sequence("test", "AB-C-D--");
+ assertEquals(1, seq.findPosition(0));
+ assertEquals(2, seq.findPosition(1));
+ // gap position 'finds' residue to the right (not the left as per javadoc)
+ assertEquals(3, seq.findPosition(2));
+ assertEquals(3, seq.findPosition(3));
+ assertEquals(4, seq.findPosition(4));
+ assertEquals(4, seq.findPosition(5));
+ // returns 1 more than sequence length if off the end ?!?
+ assertEquals(5, seq.findPosition(6));
+ assertEquals(5, seq.findPosition(7));
+
+ seq = new Sequence("test", "--AB-C-DEF--");
+ assertEquals(1, seq.findPosition(0));
+ assertEquals(1, seq.findPosition(1));
+ assertEquals(1, seq.findPosition(2));
+ assertEquals(2, seq.findPosition(3));
+ assertEquals(3, seq.findPosition(4));
+ assertEquals(3, seq.findPosition(5));
+ assertEquals(4, seq.findPosition(6));
+ assertEquals(4, seq.findPosition(7));
+ assertEquals(5, seq.findPosition(8));
+ assertEquals(6, seq.findPosition(9));
+ assertEquals(7, seq.findPosition(10));
+ assertEquals(7, seq.findPosition(11));
+ }
+
+ @Test
+ public void testDeleteChars()
+ {
+ SequenceI seq = new Sequence("test", "ABCDEF");
+ assertEquals(1, seq.getStart());
+ assertEquals(6, seq.getEnd());
+ seq.deleteChars(2, 3);
+ assertEquals("ABDEF", seq.getSequenceAsString());
+ assertEquals(1, seq.getStart());
+ assertEquals(5, seq.getEnd());
+
+ seq = new Sequence("test", "ABCDEF");
+ seq.deleteChars(0, 2);
+ assertEquals("CDEF", seq.getSequenceAsString());
+ assertEquals(3, seq.getStart());
+ assertEquals(6, seq.getEnd());
+ }
+
+ @Test
+ public void testInsertCharAt()
+ {
+ // non-static methods:
+ SequenceI seq = new Sequence("test", "ABCDEF");
+ seq.insertCharAt(0, 'z');
+ assertEquals("zABCDEF", seq.getSequenceAsString());
+ seq.insertCharAt(2, 2, 'x');
+ assertEquals("zAxxBCDEF", seq.getSequenceAsString());
+
+ // for static method see StringUtilsTest
+ }
}
--- /dev/null
+package jalview.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+
+import org.junit.Test;
+
+public class ComparisonTest
+{
+
+ @Test
+ public void testIsGap()
+ {
+ assertTrue(Comparison.isGap('-'));
+ assertTrue(Comparison.isGap('.'));
+ assertTrue(Comparison.isGap(' '));
+ assertFalse(Comparison.isGap('X'));
+ assertFalse(Comparison.isGap('x'));
+ assertFalse(Comparison.isGap('*'));
+ assertFalse(Comparison.isGap('G'));
+ }
+
+ /**
+ * Test for isNucleotide is that sequences in a dataset are more than 85%
+ * AGCTU. Test is not case-sensitive and ignores gaps.
+ */
+ @Test
+ public void testIsNucleotide() {
+ SequenceI seq = new Sequence("eightypercent", "agctuAGCPV");
+ assertFalse(Comparison.isNucleotide(new SequenceI[]
+ { seq }));
+
+ seq = new Sequence("eightyfivepercent", "agctuAGCPVagctuAGCUV");
+ assertFalse(Comparison.isNucleotide(new SequenceI[]
+ { seq }));
+
+ seq = new Sequence("nineypercent", "agctuAGCgVagctuAGCUV");
+ assertTrue(Comparison.isNucleotide(new SequenceI[]
+ { seq }));
+
+ seq = new Sequence("eightyfivepercentgapped",
+ "--agc--tuA--GCPV-a---gct-uA-GC---UV");
+ assertFalse(Comparison.isNucleotide(new SequenceI[]
+ { seq }));
+
+ seq = new Sequence("nineypercentgapped",
+ "ag--ct-u-A---GC---g----Vag--c---tuAGCUV");
+ assertTrue(Comparison.isNucleotide(new SequenceI[]
+ { seq }));
+
+ seq = new Sequence("allgap", "---------");
+ assertFalse(Comparison.isNucleotide(new SequenceI[]
+ { seq }));
+
+ seq = new Sequence("DNA", "ACTugGCCAG");
+ SequenceI seq2 = new Sequence("Protein", "FLIMVSPTYW");
+ assertTrue(Comparison.isNucleotide(new SequenceI[]
+ { seq, seq, seq, seq, seq, seq, seq, seq, seq, seq2 })); // 90% DNA
+ assertFalse(Comparison.isNucleotide(new SequenceI[]
+ { seq, seq, seq, seq, seq, seq, seq, seq, seq2, seq2 })); // 80% DNA
+
+ seq = new Sequence("ProteinThatLooksLikeDNA", "WYATGCCTGAgtcgt");
+ // 12/14 = 85.7%
+ assertTrue(Comparison.isNucleotide(new SequenceI[]
+ { seq }));
+
+ assertFalse(Comparison.isNucleotide(null));
+ }
+
+ /**
+ * Test percentage identity calculation for two sequences.
+ */
+ @Test
+ public void testPID_matchGaps()
+ {
+ String seq1 = "ABCDEF";
+ String seq2 = "abcdef";
+ assertEquals("identical", 100f, Comparison.PID(seq1, seq2), 0.001f);
+
+ // comparison range defaults to length of first sequence
+ seq2 = "abcdefghijklmnopqrstuvwxyz";
+ assertEquals("identical", 100f, Comparison.PID(seq1, seq2), 0.001f);
+
+ seq2 = "a---bcdef";
+ }
+}
--- /dev/null
+package jalview.util;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+
+import org.junit.Test;
+
+public class StringUtilsTest
+{
+
+ @Test
+ public void testInsertCharAt()
+ {
+ char[] c1 = "ABC".toCharArray();
+ char[] expected = new char[]
+ { 'A', 'B', 'C', 'w', 'w' };
+ assertTrue(Arrays.equals(expected,
+ StringUtils.insertCharAt(c1, 3, 2, 'w')));
+ expected = new char[]
+ { 'A', 'B', 'C', 'w', 'w' };
+ assertTrue(Arrays.equals(expected,
+ StringUtils.insertCharAt(c1, 4, 2, 'w')));
+ assertTrue(Arrays.equals(expected,
+ StringUtils.insertCharAt(c1, 5, 2, 'w')));
+ assertTrue(Arrays.equals(expected,
+ StringUtils.insertCharAt(c1, 6, 2, 'w')));
+ assertTrue(Arrays.equals(expected,
+ StringUtils.insertCharAt(c1, 7, 2, 'w')));
+ }
+
+ @Test
+ public void testDeleteChars()
+ {
+ char[] c1 = "ABC".toCharArray();
+
+ // delete second position
+ assertTrue(Arrays.equals(new char[]
+ { 'A', 'C' }, StringUtils.deleteChars(c1, 1, 2)));
+
+ // delete positions 1 and 2
+ assertTrue(Arrays.equals(new char[]
+ { 'C' }, StringUtils.deleteChars(c1, 0, 2)));
+
+ // delete positions 1-3
+ assertTrue(Arrays.equals(new char[]
+ {}, StringUtils.deleteChars(c1, 0, 3)));
+
+ // delete position 3
+ assertTrue(Arrays.equals(new char[]
+ { 'A', 'B' }, StringUtils.deleteChars(c1, 2, 3)));
+
+ // out of range deletion is ignore
+ assertTrue(Arrays.equals(c1, StringUtils.deleteChars(c1, 3, 4)));
+ }
+}