From: gmungoc Date: Mon, 9 Feb 2015 10:13:16 +0000 (+0000) Subject: Merge remote-tracking branch 'origin/develop' into X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=424c56371cd59181b994d7e3e8e91c4b7ba0b507;hp=2d3bf89e4c4a93d78af9772a78e94d5be7d15402;p=jalview.git Merge remote-tracking branch 'origin/develop' into features/JAL-845splitPaneMergeDevelop Conflicts: resources/lang/Messages.properties src/jalview/api/AlignViewportI.java src/jalview/appletgui/AlignFrame.java src/jalview/appletgui/AnnotationLabels.java src/jalview/appletgui/ScalePanel.java src/jalview/appletgui/SeqCanvas.java src/jalview/datamodel/ColumnSelection.java src/jalview/datamodel/Sequence.java src/jalview/ext/jmol/JalviewJmolBinding.java src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java src/jalview/ext/varna/JalviewVarnaBinding.java src/jalview/gui/AlignFrame.java src/jalview/gui/AlignViewport.java src/jalview/gui/AlignmentPanel.java src/jalview/gui/AnnotationColourChooser.java src/jalview/gui/AnnotationLabels.java src/jalview/gui/Desktop.java src/jalview/gui/Jalview2XML.java src/jalview/gui/JvSwingUtils.java src/jalview/gui/ScalePanel.java src/jalview/gui/SeqCanvas.java src/jalview/gui/SeqPanel.java src/jalview/gui/TreePanel.java src/jalview/io/AppletFormatAdapter.java src/jalview/viewmodel/AlignmentViewport.java src/jalview/ws/AWSThread.java test/jalview/ws/jabaws/JpredJabaStructExportImport.java test/jalview/ws/jabaws/RNAStructExportImport.java --- diff --git a/.classpath b/.classpath index 59772ae..f34aa90 100644 --- a/.classpath +++ b/.classpath @@ -54,5 +54,6 @@ + diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties index cfec3c9..8581238 100644 --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@ -382,6 +382,7 @@ label.automatically_associate_pdb_files_with_sequences_same_name = Do you want t label.automatically_associate_pdb_files_by_name = Automatically Associate PDB files by name label.ignore_unmatched_dropped_files_info = Do you want to ignore the {0} files whose names did not match any sequence IDs ? label.ignore_unmatched_dropped_files = Ignore unmatched dropped files? +label.view_name_original = Original label.enter_view_name = Enter View Name label.enter_label = Enter label label.enter_label_for_the_structure = Enter a label for the structure? @@ -690,11 +691,25 @@ label.load_tree_for_sequence_set = Load a tree for this sequence set label.export_image = Export Image label.vamsas_store = VAMSAS store label.translate_cDNA = Translate cDNA +label.cdna = cDNA +label.link_cdna = Link cDNA +label.link_cdna_tip = Link to any compatible cDNA alignments.
Sequences are linked that have the same name and compatible lengths. +label.no_cdna = No compatible cDNA was found +label.linked_cdna = {0} cDNA alignments linked +label.cdna_all_linked = All {0} compatible cDNA alignments are already linked +label.align_cdna = Align linked cDNA +label.align_cdna_tip = Any linked cDNA sequences will be realigned to match this alignment. +label.cdna_aligned = {0} sequences in {1} alignments were realigned +label.view_as_cdna = Show aligned cDNA +label.view_as_cdna_tip = Open a new alignment of the related cDNA sequences +label.linked_view_title = {0} and {1} +label.align = Align label.extract_scores = Extract Scores label.get_cross_refs = Get Cross References label.sort_alignment_new_tree = Sort Alignment With New Tree label.add_sequences = Add Sequences label.new_window = New Window +label.split_window = Split Window label.refresh_available_sources = Refresh Available Sources label.use_registry = Use Registry label.add_local_source = Add Local Source @@ -734,6 +749,7 @@ label.paste_new_window = Paste To New Window label.settings_for_param = Settings for {0} label.view_params = View {0} label.select_all_views = Select all views +label.all_views = All Views label.align_sequences_to_existing_alignment = Align sequences to an existing alignment label.realign_with_params = Realign with {0} label.calcname_with_default_settings = {0} with Defaults @@ -1178,6 +1194,12 @@ label.show_logo = Show Logo label.normalise_logo = Normalise Logo label.no_colour_selection_in_scheme = Please, make a colour selection before to apply colour scheme label.no_colour_selection_warn = Error saving colour scheme +label.open_linked_alignment? = Would you like to open as a separate alignment, with cDNA and protein linked? +label.open_linked_alignment = Open linked alignment +label.no_mappings = No mappings found +label.mapping_failed = No sequence mapping could be made between the alignments.
A mapping requires sequence names to match, and equivalent sequence lengths. +action.no = No +label.for = for label.select_by_annotation = Select By Annotation action.select_by_annotation = Select by Annotation... label.threshold_filter = Threshold Filter @@ -1191,3 +1213,4 @@ label.structures_filter = Structures Filter label.search_filter = Search Filter label.display_name = Display Label label.description = Description + diff --git a/src/MCview/AppletPDBCanvas.java b/src/MCview/AppletPDBCanvas.java index 9b0412a..1938d9a 100644 --- a/src/MCview/AppletPDBCanvas.java +++ b/src/MCview/AppletPDBCanvas.java @@ -20,19 +20,34 @@ */ 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 @@ -144,7 +159,9 @@ public class AppletPDBCanvas extends Panel implements MouseListener, pdb = ssm.setMapping(seq, chains, pdbentry.getFile(), protocol); if (protocol.equals(jalview.io.AppletFormatAdapter.PASTE)) + { pdbentry.setFile("INLINE" + pdb.id); + } } catch (Exception ex) { @@ -172,10 +189,10 @@ public class AppletPDBCanvas extends Panel implements MouseListener, { 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 @@ -183,8 +200,8 @@ public class AppletPDBCanvas extends Panel implements MouseListener, // 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(); @@ -218,7 +235,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener, mappingDetails.append("\nSEQ start/end " + seqstart + " " + seqend); } - mainchain = (PDBChain) pdb.chains.elementAt(maxchain); + mainchain = pdb.chains.elementAt(maxchain); mainchain.pdbstart = pdbstart; mainchain.pdbend = pdbend; @@ -277,9 +294,9 @@ public class AppletPDBCanvas extends Panel implements MouseListener, 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++) { @@ -308,9 +325,9 @@ public class AppletPDBCanvas extends Panel implements MouseListener, 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++) { @@ -379,9 +396,9 @@ public class AppletPDBCanvas extends Panel implements MouseListener, } } - 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]; @@ -438,9 +455,9 @@ public class AppletPDBCanvas extends Panel implements MouseListener, // 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(); @@ -567,7 +584,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener, { 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++) { @@ -779,7 +796,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener, repaint(); if (foundchain != -1) { - PDBChain chain = (PDBChain) pdb.chains.elementAt(foundchain); + PDBChain chain = pdb.chains.elementAt(foundchain); if (chain == mainchain) { if (fatom.alignmentMapping != -1) @@ -825,7 +842,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener, 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); @@ -875,18 +892,18 @@ public class AppletPDBCanvas extends Panel implements MouseListener, 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++) { @@ -927,11 +944,11 @@ public class AppletPDBCanvas extends Panel implements MouseListener, 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++) { @@ -985,13 +1002,13 @@ public class AppletPDBCanvas extends Panel implements MouseListener, 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++) { @@ -1032,7 +1049,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener, if (fatom != null) // )&& chain.ds != null) { - chain = (PDBChain) pdb.chains.elementAt(foundchain); + chain = pdb.chains.elementAt(foundchain); } } @@ -1100,7 +1117,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener, { 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; @@ -1121,7 +1138,9 @@ public class AppletPDBCanvas extends Panel implements MouseListener, public void mouseOverStructure(int pdbResNum, String chain) { if (lastMessage == null || !lastMessage.equals(pdbResNum + chain)) + { ssm.mouseOverStructure(pdbResNum, chain, pdbentry.getFile()); + } lastMessage = pdbResNum + chain; } @@ -1130,19 +1149,40 @@ public class AppletPDBCanvas extends Panel implements MouseListener, 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 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++) @@ -1178,9 +1218,6 @@ public class AppletPDBCanvas extends Panel implements MouseListener, break; } } - - redrawneeded = true; - repaint(); } public Color getColour(int atomIndex, int pdbResNum, String chain, diff --git a/src/MCview/PDBCanvas.java b/src/MCview/PDBCanvas.java index 4fd7a35..f189f0a 100644 --- a/src/MCview/PDBCanvas.java +++ b/src/MCview/PDBCanvas.java @@ -20,18 +20,37 @@ */ 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 @@ -134,7 +153,9 @@ public class PDBCanvas extends JPanel implements MouseListener, pdb = ssm.setMapping(seq, chains, pdbentry.getFile(), protocol); if (protocol.equals(jalview.io.AppletFormatAdapter.PASTE)) + { pdbentry.setFile("INLINE" + pdb.id); + } } catch (Exception ex) { @@ -167,17 +188,17 @@ public class PDBCanvas extends JPanel implements MouseListener, { 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) @@ -210,7 +231,7 @@ public class PDBCanvas extends JPanel implements MouseListener, mappingDetails.append("\nSEQ start/end " + seqstart + " " + seqend); } - mainchain = (PDBChain) pdb.chains.elementAt(maxchain); + mainchain = pdb.chains.elementAt(maxchain); mainchain.pdbstart = pdbstart; mainchain.pdbend = pdbend; @@ -254,9 +275,9 @@ public class PDBCanvas extends JPanel implements MouseListener, 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++) { @@ -286,9 +307,9 @@ public class PDBCanvas extends JPanel implements MouseListener, 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++) { @@ -362,9 +383,9 @@ public class PDBCanvas extends JPanel implements MouseListener, * 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]; @@ -421,9 +442,9 @@ public class PDBCanvas extends JPanel implements MouseListener, // 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(); @@ -537,7 +558,7 @@ public class PDBCanvas extends JPanel implements MouseListener, { 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++) { @@ -751,7 +772,7 @@ public class PDBCanvas extends JPanel implements MouseListener, repaint(); if (foundchain != -1) { - PDBChain chain = (PDBChain) pdb.chains.elementAt(foundchain); + PDBChain chain = pdb.chains.elementAt(foundchain); if (chain == mainchain) { if (fatom.alignmentMapping != -1) @@ -797,7 +818,7 @@ public class PDBCanvas extends JPanel implements MouseListener, 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); @@ -840,18 +861,18 @@ public class PDBCanvas extends JPanel implements MouseListener, 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++) { @@ -892,11 +913,11 @@ public class PDBCanvas extends JPanel implements MouseListener, 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++) { @@ -948,13 +969,13 @@ public class PDBCanvas extends JPanel implements MouseListener, 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++) { @@ -995,7 +1016,7 @@ public class PDBCanvas extends JPanel implements MouseListener, if (fatom != null) // )&& chain.ds != null) { - chain = (PDBChain) pdb.chains.elementAt(foundchain); + chain = pdb.chains.elementAt(foundchain); } } @@ -1060,7 +1081,7 @@ public class PDBCanvas extends JPanel implements MouseListener, { 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; @@ -1081,7 +1102,9 @@ public class PDBCanvas extends JPanel implements MouseListener, public void mouseOverStructure(int pdbResNum, String chain) { if (lastMessage == null || !lastMessage.equals(pdbResNum + chain)) + { ssm.mouseOverStructure(pdbResNum, chain, pdbentry.getFile()); + } lastMessage = pdbResNum + chain; } @@ -1090,19 +1113,42 @@ public class PDBCanvas extends JPanel implements MouseListener, 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 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++) @@ -1138,9 +1184,6 @@ public class PDBCanvas extends JPanel implements MouseListener, break; } } - - redrawneeded = true; - repaint(); } public Color getColour(int atomIndex, int pdbResNum, String chain, diff --git a/src/jalview/analysis/AlignSeq.java b/src/jalview/analysis/AlignSeq.java index ba7e520..ab03668 100755 --- a/src/jalview/analysis/AlignSeq.java +++ b/src/jalview/analysis/AlignSeq.java @@ -804,19 +804,23 @@ public class AlignSeq } /** - * DOCUMENT ME! + * Returns the given sequence with all of the given gap characters removed. * - * @param gapChar - * DOCUMENT ME! + * @param gapChars + * a string of characters to be treated as gaps * @param seq - * DOCUMENT ME! + * the input sequence * - * @return DOCUMENT ME! + * @return */ - public static String extractGaps(String gapChar, String seq) + public static String extractGaps(String gapChars, String seq) { - StringTokenizer str = new StringTokenizer(seq, gapChar); - StringBuffer newString = new StringBuffer(); + if (gapChars == null || seq == null) + { + return null; + } + StringTokenizer str = new StringTokenizer(seq, gapChars); + StringBuilder newString = new StringBuilder(seq.length()); while (str.hasMoreTokens()) { diff --git a/src/jalview/analysis/AlignmentSorter.java b/src/jalview/analysis/AlignmentSorter.java index b7cfbbd..4f52741 100755 --- a/src/jalview/analysis/AlignmentSorter.java +++ b/src/jalview/analysis/AlignmentSorter.java @@ -20,10 +20,19 @@ */ package jalview.analysis; -import java.util.*; - -import jalview.datamodel.*; -import jalview.util.*; +import jalview.datamodel.AlignmentAnnotation; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.AlignmentOrder; +import jalview.datamodel.SequenceFeature; +import jalview.datamodel.SequenceGroup; +import jalview.datamodel.SequenceI; +import jalview.datamodel.SequenceNode; +import jalview.util.Comparison; +import jalview.util.MessageManager; +import jalview.util.QuickSort; + +import java.util.ArrayList; +import java.util.List; /** * Routines for manipulating the order of a multiple sequence alignment TODO: @@ -169,7 +178,7 @@ public class AlignmentSorter * @param tmp * sequences as a vector */ - private static void setOrder(AlignmentI align, Vector tmp) + private static void setOrder(AlignmentI align, List tmp) { setOrder(align, vectorSubsetToArray(tmp, align.getSequences())); } @@ -285,7 +294,7 @@ public class AlignmentSorter { // MAINTAINS ORIGNAL SEQUENCE ORDER, // ORDERS BY GROUP SIZE - Vector groups = new Vector(); + List groups = new ArrayList(); if (groups.hashCode() != lastGroupHash) { @@ -303,11 +312,11 @@ public class AlignmentSorter { for (int j = 0; j < groups.size(); j++) { - SequenceGroup sg2 = (SequenceGroup) groups.elementAt(j); + SequenceGroup sg2 = groups.get(j); if (sg.getSize() > sg2.getSize()) { - groups.insertElementAt(sg, j); + groups.add(j, sg); break; } @@ -315,22 +324,22 @@ public class AlignmentSorter if (!groups.contains(sg)) { - groups.addElement(sg); + groups.add(sg); } } // NOW ADD SEQUENCES MAINTAINING ALIGNMENT ORDER // ///////////////////////////////////////////// - Vector seqs = new Vector(); + List seqs = new ArrayList(); for (int i = 0; i < groups.size(); i++) { - SequenceGroup sg = (SequenceGroup) groups.elementAt(i); + SequenceGroup sg = groups.get(i); SequenceI[] orderedseqs = sg.getSequencesInOrder(align); for (int j = 0; j < orderedseqs.length; j++) { - seqs.addElement(orderedseqs[j]); + seqs.add(orderedseqs[j]); } } @@ -346,28 +355,8 @@ public class AlignmentSorter } /** - * Converts Vector to array. java 1.18 does not have Vector.toArray() - * - * @param tmp - * Vector of SequenceI objects - * - * @return array of Sequence[] - */ - private static SequenceI[] vectorToArray(Vector tmp) - { - SequenceI[] seqs = new SequenceI[tmp.size()]; - - for (int i = 0; i < tmp.size(); i++) - { - seqs[i] = (SequenceI) tmp.elementAt(i); - } - - return seqs; - } - - /** * Select sequences in order from tmp that is present in mask, and any - * remaining seqeunces in mask not in tmp + * remaining sequences in mask not in tmp * * @param tmp * thread safe collection of sequences @@ -379,6 +368,10 @@ public class AlignmentSorter private static SequenceI[] vectorSubsetToArray(List tmp, List mask) { + // or? + // tmp2 = tmp.retainAll(mask); + // return tmp2.addAll(mask.removeAll(tmp2)) + ArrayList seqs = new ArrayList(); int i, idx; boolean[] tmask = new boolean[mask.size()]; @@ -421,7 +414,7 @@ public class AlignmentSorter public static void sortBy(AlignmentI align, AlignmentOrder order) { // Get an ordered vector of sequences which may also be present in align - Vector tmp = order.getOrder(); + List tmp = order.getOrder(); if (lastOrder == order) { @@ -452,11 +445,12 @@ public class AlignmentSorter * * @return DOCUMENT ME! */ - private static Vector getOrderByTree(AlignmentI align, NJTree tree) + private static List getOrderByTree(AlignmentI align, + NJTree tree) { int nSeq = align.getHeight(); - Vector tmp = new Vector(); + List tmp = new ArrayList(); tmp = _sortByTree(tree.getTopNode(), tmp, align.getSequences()); @@ -494,7 +488,7 @@ public class AlignmentSorter */ public static void sortByTree(AlignmentI align, NJTree tree) { - Vector tmp = getOrderByTree(align, tree); + List tmp = getOrderByTree(align, tree); // tmp should properly permute align with tree. if (lastTree != tree) @@ -522,22 +516,22 @@ public class AlignmentSorter * * @param align * DOCUMENT ME! - * @param seqs + * @param tmp * DOCUMENT ME! */ - private static void addStrays(AlignmentI align, Vector seqs) + private static void addStrays(AlignmentI align, List tmp) { int nSeq = align.getHeight(); for (int i = 0; i < nSeq; i++) { - if (!seqs.contains(align.getSequenceAt(i))) + if (!tmp.contains(align.getSequenceAt(i))) { - seqs.addElement(align.getSequenceAt(i)); + tmp.add(align.getSequenceAt(i)); } } - if (nSeq != seqs.size()) + if (nSeq != tmp.size()) { System.err .println("ERROR: Size still not right even after addStrays"); @@ -556,7 +550,8 @@ public class AlignmentSorter * * @return DOCUMENT ME! */ - private static Vector _sortByTree(SequenceNode node, Vector tmp, + private static List _sortByTree(SequenceNode node, + List tmp, List seqset) { if (node == null) @@ -577,7 +572,7 @@ public class AlignmentSorter // seqset.size()==0 || // seqset.contains(tmp))) { - tmp.addElement(node.element()); + tmp.add((SequenceI) node.element()); } } } diff --git a/src/jalview/analysis/AlignmentUtils.java b/src/jalview/analysis/AlignmentUtils.java index 6385fa7..7116af9 100644 --- a/src/jalview/analysis/AlignmentUtils.java +++ b/src/jalview/analysis/AlignmentUtils.java @@ -20,12 +20,18 @@ */ package jalview.analysis; +import jalview.datamodel.AlignedCodonFrame; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.SequenceI; +import jalview.schemes.ResidueProperties; +import jalview.util.MapList; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; +import java.util.Set; /** * grab bag of useful alignment manipulation operations Expect these to be @@ -38,6 +44,15 @@ public class AlignmentUtils { /** + * Represents the 3 possible results of trying to map one alignment to + * another. + */ + public enum MappingResult + { + Mapped, NotMapped, AlreadyMapped + } + + /** * given an existing alignment, create a new alignment including all, or up to * flankSize additional symbols from each sequence's dataset sequence * @@ -159,4 +174,456 @@ public class AlignmentUtils } return result; } + + /** + * Returns a map of lists of sequences in the alignment, keyed by sequence + * name. For use in mapping between different alignment views of the same + * sequences. + * + * @see jalview.datamodel.AlignmentI#getSequencesByName() + */ + public static Map> getSequencesByName( + AlignmentI al) + { + Map> theMap = new LinkedHashMap>(); + for (SequenceI seq : al.getSequences()) + { + String name = seq.getName(); + if (name != null) + { + List seqs = theMap.get(name); + if (seqs == null) + { + seqs = new ArrayList(); + theMap.put(name, seqs); + } + seqs.add(seq); + } + } + return theMap; + } + + /** + * Build mapping of protein to cDNA alignment. Mappings are made between + * sequences which have the same name and compatible lengths. Has a 3-valued + * result: either Mapped (at least one sequence mapping was created), + * AlreadyMapped (all possible sequence mappings already exist), or NotMapped + * (no possible sequence mappings exist). + * + * @param proteinAlignment + * @param cdnaAlignment + * @return + */ + public static MappingResult mapProteinToCdna( + final AlignmentI proteinAlignment, + final AlignmentI cdnaAlignment) + { + boolean mappingPossible = false; + boolean mappingPerformed = false; + + List thisSeqs = proteinAlignment.getSequences(); + + /* + * Build a look-up of cDNA sequences by name, for matching purposes. + */ + Map> cdnaSeqs = cdnaAlignment + .getSequencesByName(); + + for (SequenceI aaSeq : thisSeqs) + { + AlignedCodonFrame acf = new AlignedCodonFrame(); + List candidates = cdnaSeqs.get(aaSeq.getName()); + if (candidates == null) + { + /* + * No cDNA sequence with matching name, so no mapping possible for this + * protein sequence + */ + continue; + } + mappingPossible = true; + for (SequenceI cdnaSeq : candidates) + { + if (!mappingExists(proteinAlignment.getCodonFrames(), + aaSeq.getDatasetSequence(), cdnaSeq.getDatasetSequence())) + { + MapList map = mapProteinToCdna(aaSeq, cdnaSeq); + if (map != null) + { + acf.addMap(cdnaSeq, aaSeq, map); + mappingPerformed = true; + } + } + } + proteinAlignment.addCodonFrame(acf); + } + + /* + * If at least one mapping was possible but none was done, then the + * alignments are already as mapped as they can be. + */ + if (mappingPossible && !mappingPerformed) + { + return MappingResult.AlreadyMapped; + } + else + { + return mappingPerformed ? MappingResult.Mapped + : MappingResult.NotMapped; + } + } + + /** + * Answers true if the mappings include one between the given (dataset) + * sequences. + */ + public static boolean mappingExists(Set set, + SequenceI aaSeq, SequenceI cdnaSeq) + { + if (set != null) + { + for (AlignedCodonFrame acf : set) + { + if (cdnaSeq == acf.getDnaForAaSeq(aaSeq)) + { + return true; + } + } + } + return false; + } + + /** + * Build a mapping (if possible) of a protein to a cDNA sequence. The cDNA + * must be three times the length of the protein, possibly after ignoring + * start and/or stop codons. Returns null if no mapping is determined. + * + * @param proteinSeqs + * @param cdnaSeq + * @return + */ + public static MapList mapProteinToCdna(SequenceI proteinSeq, + SequenceI cdnaSeq) + { + String aaSeqString = proteinSeq.getDatasetSequence() + .getSequenceAsString(); + String cdnaSeqString = cdnaSeq.getDatasetSequence() + .getSequenceAsString(); + if (aaSeqString == null || cdnaSeqString == null) + { + return null; + } + + final int mappedLength = 3 * aaSeqString.length(); + int cdnaLength = cdnaSeqString.length(); + int cdnaStart = 1; + int cdnaEnd = cdnaLength; + final int proteinStart = 1; + final int proteinEnd = aaSeqString.length(); + + /* + * If lengths don't match, try ignoring stop codon. + */ + if (cdnaLength != mappedLength) + { + for (Object stop : ResidueProperties.STOP) + { + if (cdnaSeqString.toUpperCase().endsWith((String) stop)) + { + cdnaEnd -= 3; + cdnaLength -= 3; + break; + } + } + } + + /* + * If lengths still don't match, try ignoring start codon. + */ + if (cdnaLength != mappedLength + && cdnaSeqString.toUpperCase().startsWith( + ResidueProperties.START)) + { + cdnaStart += 3; + cdnaLength -= 3; + } + + if (cdnaLength == mappedLength) + { + MapList map = new MapList(new int[] + { cdnaStart, cdnaEnd }, new int[] + { proteinStart, proteinEnd }, 3, 1); + return map; + } + else + { + return null; + } + } + + /** + * Align sequence 'seq' to match the alignment of a mapped sequence. Note this + * currently assumes that we are aligning cDNA to match protein. + * + * @param seq + * the sequence to be realigned + * @param al + * the alignment whose sequence alignment is to be 'copied' + * @param gap + * character string represent a gap in the realigned sequence + * @param preserveUnmappedGaps + * @param preserveMappedGaps + * @return true if the sequence was realigned, false if it could not be + */ + public static boolean alignSequenceAs(SequenceI seq, AlignmentI al, + String gap, boolean preserveMappedGaps, + boolean preserveUnmappedGaps) + { + /* + * Get any mappings from the source alignment to the target (dataset) sequence. + */ + // TODO there may be one AlignedCodonFrame per dataset sequence, or one with + // all mappings. Would it help to constrain this? + List mappings = al.getCodonFrame(seq); + if (mappings == null) + { + return false; + } + + /* + * Locate the aligned source sequence whose dataset sequence is mapped. We + * just take the first match here (as we can't align cDNA like more than one + * protein sequence). + */ + SequenceI alignFrom = null; + AlignedCodonFrame mapping = null; + for (AlignedCodonFrame mp : mappings) + { + alignFrom = mp.findAlignedSequence(seq.getDatasetSequence(), al); + if (alignFrom != null) + { + mapping = mp; + break; + } + } + + if (alignFrom == null) + { + return false; + } + alignSequenceAs(seq, alignFrom, mapping, gap, al.getGapCharacter(), + preserveMappedGaps, preserveUnmappedGaps); + return true; + } + + /** + * Align sequence 'alignTo' the same way as 'alignFrom', using the mapping to + * match residues and codons. Flags control whether existing gaps in unmapped + * (intron) and mapped (exon) regions are preserved or not. Gaps linking intro + * and exon are only retained if both flags are set. + * + * @param alignTo + * @param alignFrom + * @param mapping + * @param myGap + * @param sourceGap + * @param preserveUnmappedGaps + * @param preserveMappedGaps + */ + public static void alignSequenceAs(SequenceI alignTo, + SequenceI alignFrom, + AlignedCodonFrame mapping, String myGap, char sourceGap, + boolean preserveMappedGaps, boolean preserveUnmappedGaps) + { + // TODO generalise to work for Protein-Protein, dna-dna, dna-protein + final char[] thisSeq = alignTo.getSequence(); + final char[] thatAligned = alignFrom.getSequence(); + StringBuilder thisAligned = new StringBuilder(2 * thisSeq.length); + + // aligned and dataset sequence positions, all base zero + int thisSeqPos = 0; + int sourceDsPos = 0; + + int basesWritten = 0; + char myGapChar = myGap.charAt(0); + int ratio = myGap.length(); + + /* + * Traverse the aligned protein sequence. + */ + int sourceGapMappedLength = 0; + boolean inExon = false; + for (char sourceChar : thatAligned) + { + if (sourceChar == sourceGap) + { + sourceGapMappedLength += ratio; + continue; + } + + /* + * Found a residue. Locate its mapped codon (start) position. + */ + sourceDsPos++; + // Note mapping positions are base 1, our sequence positions base 0 + int[] mappedPos = mapping.getMappedRegion(alignTo, alignFrom, + sourceDsPos); + if (mappedPos == null) + { + /* + * Abort realignment if unmapped protein. Or could ignore it?? + */ + System.err.println("Can't align: no codon mapping to residue " + + sourceDsPos + "(" + sourceChar + ")"); + return; + } + + int mappedCodonStart = mappedPos[0]; // position (1...) of codon start + int mappedCodonEnd = mappedPos[mappedPos.length - 1]; // codon end pos + StringBuilder trailingCopiedGap = new StringBuilder(); + + /* + * Copy dna sequence up to and including this codon. Optionally, include + * gaps before the codon starts (in introns) and/or after the codon starts + * (in exons). + * + * Note this only works for 'linear' splicing, not reverse or interleaved. + * But then 'align dna as protein' doesn't make much sense otherwise. + */ + int intronLength = 0; + while (basesWritten < mappedCodonEnd && thisSeqPos < thisSeq.length) + { + final char c = thisSeq[thisSeqPos++]; + if (c != myGapChar) + { + basesWritten++; + + if (basesWritten < mappedCodonStart) + { + /* + * Found an unmapped (intron) base. First add in any preceding gaps + * (if wanted). + */ + if (preserveUnmappedGaps && trailingCopiedGap.length() > 0) + { + thisAligned.append(trailingCopiedGap.toString()); + intronLength += trailingCopiedGap.length(); + trailingCopiedGap = new StringBuilder(); + } + intronLength++; + inExon = false; + } + else + { + final boolean startOfCodon = basesWritten == mappedCodonStart; + int gapsToAdd = calculateGapsToInsert(preserveMappedGaps, + preserveUnmappedGaps, sourceGapMappedLength, inExon, + trailingCopiedGap.length(), intronLength, startOfCodon); + for (int i = 0; i < gapsToAdd; i++) + { + thisAligned.append(myGapChar); + } + sourceGapMappedLength = 0; + inExon = true; + } + thisAligned.append(c); + trailingCopiedGap = new StringBuilder(); + } + else + { + if (inExon && preserveMappedGaps) + { + trailingCopiedGap.append(myGapChar); + } + else if (!inExon && preserveUnmappedGaps) + { + trailingCopiedGap.append(myGapChar); + } + } + } + } + + /* + * At end of protein sequence. Copy any remaining dna sequence, optionally + * including (intron) gaps. We do not copy trailing gaps in protein. + */ + while (thisSeqPos < thisSeq.length) + { + final char c = thisSeq[thisSeqPos++]; + if (c != myGapChar || preserveUnmappedGaps) + { + thisAligned.append(c); + } + } + + /* + * All done aligning, set the aligned sequence. + */ + alignTo.setSequence(new String(thisAligned)); + } + + /** + * Helper method to work out how many gaps to insert when realigning. + * + * @param preserveMappedGaps + * @param preserveUnmappedGaps + * @param sourceGapMappedLength + * @param inExon + * @param trailingCopiedGap + * @param intronLength + * @param startOfCodon + * @return + */ + protected static int calculateGapsToInsert(boolean preserveMappedGaps, + boolean preserveUnmappedGaps, int sourceGapMappedLength, + boolean inExon, int trailingGapLength, + int intronLength, final boolean startOfCodon) + { + int gapsToAdd = 0; + if (startOfCodon) + { + /* + * Reached start of codon. Ignore trailing gaps in intron unless we are + * preserving gaps in both exon and intron. Ignore them anyway if the + * protein alignment introduces a gap at least as large as the intronic + * region. + */ + if (inExon && !preserveMappedGaps) + { + trailingGapLength = 0; + } + if (!inExon && !(preserveMappedGaps && preserveUnmappedGaps)) + { + trailingGapLength = 0; + } + if (inExon) + { + gapsToAdd = Math.max(sourceGapMappedLength, trailingGapLength); + } + else + { + if (intronLength + trailingGapLength <= sourceGapMappedLength) + { + gapsToAdd = sourceGapMappedLength - intronLength; + } + else + { + gapsToAdd = Math.min(intronLength + trailingGapLength + - sourceGapMappedLength, trailingGapLength); + } + } + } + else + { + /* + * second or third base of codon; check for any gaps in dna + */ + if (!preserveMappedGaps) + { + trailingGapLength = 0; + } + gapsToAdd = Math.max(sourceGapMappedLength, trailingGapLength); + } + return gapsToAdd; + } } diff --git a/src/jalview/analysis/CodonComparator.java b/src/jalview/analysis/CodonComparator.java new file mode 100644 index 0000000..fc196de --- /dev/null +++ b/src/jalview/analysis/CodonComparator.java @@ -0,0 +1,91 @@ +package jalview.analysis; + +import jalview.datamodel.AlignedCodon; + +import java.util.Comparator; + +/** + * Implements rules for comparing two aligned codons, i.e. determining whether + * they should occupy the same position in a translated protein alignment, or + * one or the other should 'follow' (by preceded by a gap). + * + * @author gmcarstairs + * + */ +public final class CodonComparator implements Comparator +{ + + @Override + public int compare(AlignedCodon ac1, AlignedCodon ac2) + { + if (ac1 == null || ac2 == null || ac1.equals(ac2)) + { + return 0; + } + + /** + *
+     * Case 1: if one starts before the other, and doesn't end after it, then it
+     * precedes. We ignore the middle base position here.
+     * A--GT
+     * -CT-G
+     * 
+ */ + if (ac1.pos1 < ac2.pos1 && ac1.pos3 <= ac2.pos3) + { + return -1; + } + if (ac2.pos1 < ac1.pos1 && ac2.pos3 <= ac1.pos3) + { + return 1; + } + + /** + *
+     * Case 2: if one ends after the other, and doesn't start before it, then it
+     * follows. We ignore the middle base position here.
+     * -TG-A
+     * G-TC
+     * 
+ */ + if (ac1.pos3 > ac2.pos3 && ac1.pos1 >= ac2.pos1) + { + return 1; + } + if (ac2.pos3 > ac1.pos3 && ac2.pos1 >= ac1.pos1) + { + return -1; + } + + /* + * Case 3: if start and end match, compare middle base positions. + */ + if (ac1.pos1 == ac2.pos1 && ac1.pos3 == ac2.pos3) + { + return Integer.compare(ac1.pos2, ac2.pos2); + } + + /* + * That just leaves the 'enclosing' case - one codon starts after but ends + * before the other. If the middle bases don't match, use their comparison + * (majority vote). + */ + int compareMiddles = Integer.compare(ac1.pos2, ac2.pos2); + if (compareMiddles != 0) + { + return compareMiddles; + } + + /** + *
+     * Finally just leaves overlap with matching middle base, e.g. 
+     * -A-A-A
+     * G--GG 
+     * In this case the choice is arbitrary whether to compare based on
+     * first or last base position. We pick the first. Note this preserves
+     * symmetricality of the comparison.
+     * 
+ */ + return Integer.compare(ac1.pos1, ac2.pos1); + } +} diff --git a/src/jalview/analysis/CrossRef.java b/src/jalview/analysis/CrossRef.java index fa0fe2f..435a477 100644 --- a/src/jalview/analysis/CrossRef.java +++ b/src/jalview/analysis/CrossRef.java @@ -20,21 +20,21 @@ */ package jalview.analysis; -import java.util.Enumeration; -import java.util.List; -import java.util.Vector; -import java.util.Hashtable; - import jalview.datamodel.AlignedCodonFrame; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentI; -import jalview.datamodel.DBRefSource; import jalview.datamodel.DBRefEntry; +import jalview.datamodel.DBRefSource; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceI; import jalview.ws.SequenceFetcher; import jalview.ws.seqfetcher.ASequenceFetcher; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.List; +import java.util.Vector; + /** * Functions for cross-referencing sequence databases. user must first specify * if cross-referencing from protein or dna (set dna==true) @@ -230,7 +230,7 @@ public class CrossRef { Vector rseqs = new Vector(); Alignment ral = null; - AlignedCodonFrame cf = new AlignedCodonFrame(0); // nominal width + AlignedCodonFrame cf = new AlignedCodonFrame(); // nominal width for (int s = 0; s < seqs.length; s++) { SequenceI dss = seqs[s]; @@ -258,7 +258,9 @@ public class CrossRef for (int r = 0; xrfs != null && r < xrfs.length; r++) { if (source != null && !source.equals(xrfs[r].getSource())) + { continue; + } if (xrfs[r].hasMap()) { if (xrfs[r].getMap().getTo() != null) @@ -291,7 +293,9 @@ public class CrossRef { found |= searchDataset(dss, xrfs[r], dataset, rseqs, cf); // ,false,!dna); if (found) + { xrfs[r] = null; // we've recovered seqs for this one. + } } } } @@ -328,7 +332,9 @@ public class CrossRef for (int r = 0; r < xrfs.length; r++) { if (xrfs[r] != null) + { t[l++] = xrfs[r]; + } } xrfs = t; try @@ -432,7 +438,9 @@ public class CrossRef { boolean found = false; if (lrfs == null) + { return false; + } for (int i = 0; i < lrfs.length; i++) { DBRefEntry xref = new DBRefEntry(lrfs[i]); @@ -484,7 +492,9 @@ public class CrossRef boolean found = false; SequenceI[] typer = new SequenceI[1]; if (dataset == null) + { return false; + } if (dataset.getSequences() == null) { System.err.println("Empty dataset sequence set - NO VECTOR"); @@ -494,6 +504,7 @@ public class CrossRef synchronized (ds = dataset.getSequences()) { for (SequenceI nxt : ds) + { if (nxt != null) { if (nxt.getDatasetSequence() != null) @@ -566,6 +577,7 @@ public class CrossRef } } + } } return found; } diff --git a/src/jalview/analysis/Dna.java b/src/jalview/analysis/Dna.java index 2e56e67..2ef63d4 100644 --- a/src/jalview/analysis/Dna.java +++ b/src/jalview/analysis/Dna.java @@ -20,120 +20,157 @@ */ package jalview.analysis; -import java.util.ArrayList; -import java.util.Hashtable; -import java.util.Vector; - +import jalview.api.AlignViewportI; +import jalview.bin.Cache; +import jalview.datamodel.AlignedCodon; import jalview.datamodel.AlignedCodonFrame; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.Annotation; import jalview.datamodel.DBRefEntry; +import jalview.datamodel.DBRefSource; import jalview.datamodel.FeatureProperties; +import jalview.datamodel.GraphLine; import jalview.datamodel.Mapping; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; import jalview.schemes.ResidueProperties; +import jalview.util.Comparison; +import jalview.util.DBRefUtils; import jalview.util.MapList; import jalview.util.ShiftList; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + public class Dna { - /** + private static final String STOP_X = "X"; + + private static final Comparator comparator = new CodonComparator(); + + /* + * 'final' variables describe the inputs to the translation, which should not + * be modified. + */ + final private List selection; + + final private String[] seqstring; + + final private int[] contigs; + + final private char gapChar; + + final private AlignmentAnnotation[] annotations; + + final private int dnaWidth; + + final private Alignment dataset; + + /* + * Working variables for the translation. * - * @param cdp1 - * @param cdp2 - * @return -1 if cdp1 aligns before cdp2, 0 if in the same column or cdp2 is - * null, +1 if after cdp2 + * The width of the translation-in-progress protein alignment. */ - private static int compare_codonpos(int[] cdp1, int[] cdp2) - { - if (cdp2 == null - || (cdp1[0] == cdp2[0] && cdp1[1] == cdp2[1] && cdp1[2] == cdp2[2])) - return 0; - if (cdp1[0] < cdp2[0] || cdp1[1] < cdp2[1] || cdp1[2] < cdp2[2]) - return -1; // one base in cdp1 precedes the corresponding base in the - // other codon - return 1; // one base in cdp1 appears after the corresponding base in the - // other codon. - } + private int aaWidth = 0; - /** - * DNA->mapped protein sequence alignment translation given set of sequences - * 1. id distinct coding regions within selected region for each sequence 2. - * generate peptides based on inframe (or given) translation or (optionally - * and where specified) out of frame translations (annotated appropriately) 3. - * align peptides based on codon alignment + /* + * This array will be built up so that position i holds the codon positions + * e.g. [7, 9, 10] that match column i (base 0) in the aligned translation. + * Note this implies a contract that if two codons do not align exactly, their + * translated products must occupy different column positions. */ + private AlignedCodon[] alignedCodons; + /** - * id potential products from dna 1. search for distinct products within - * selected region for each selected sequence 2. group by associated DB type. - * 3. return as form for input into above function + * Constructor given a viewport and the visible contigs. + * + * @param viewport + * @param visibleContigs */ + public Dna(AlignViewportI viewport, int[] visibleContigs) + { + this.selection = Arrays.asList(viewport.getSequenceSelection()); + this.seqstring = viewport.getViewAsString(true); + this.contigs = visibleContigs; + this.gapChar = viewport.getGapCharacter(); + this.annotations = viewport.getAlignment().getAlignmentAnnotation(); + this.dnaWidth = viewport.getAlignment().getWidth(); + this.dataset = viewport.getAlignment().getDataset(); + } + /** + * Test whether codon positions cdp1 should align before, with, or after cdp2. + * Returns zero if all positions match (or either argument is null). Returns + * -1 if any position in the first codon precedes the corresponding position + * in the second codon. Else returns +1 (some position in the second codon + * precedes the corresponding position in the first). + * + * Note this is not necessarily symmetric, for example: + *
    + *
  • compareCodonPos([2,5,6], [3,4,5]) returns -1
  • + *
  • compareCodonPos([3,4,5], [2,5,6]) also returns -1
  • + *
* + * @param ac1 + * @param ac2 + * @return */ + public static final int compareCodonPos(AlignedCodon ac1, AlignedCodon ac2) + { + return comparator.compare(ac1, ac2); + // return jalview_2_8_2compare(ac1, ac2); + } + /** - * create a new alignment of protein sequences by an inframe translation of - * the provided NA sequences + * Codon comparison up to Jalview 2.8.2. This rule is sequence order dependent + * - see http://issues.jalview.org/browse/JAL-1635 * - * @param selection - * @param seqstring - * @param viscontigs - * @param gapCharacter - * @param annotations - * @param aWidth - * @param dataset - * destination dataset for translated sequences and mappings + * @param ac1 + * @param ac2 * @return */ - public static AlignmentI CdnaTranslate(SequenceI[] selection, - String[] seqstring, int viscontigs[], char gapCharacter, - AlignmentAnnotation[] annotations, int aWidth, Alignment dataset) + private static int jalview_2_8_2compare(AlignedCodon ac1, AlignedCodon ac2) { - return CdnaTranslate(selection, seqstring, null, viscontigs, - gapCharacter, annotations, aWidth, dataset); + if (ac1 == null || ac2 == null || (ac1.equals(ac2))) + { + return 0; + } + if (ac1.pos1 < ac2.pos1 || ac1.pos2 < ac2.pos2 || ac1.pos3 < ac2.pos3) + { + // one base in cdp1 precedes the corresponding base in the other codon + return -1; + } + // one base in cdp1 appears after the corresponding base in the other codon. + return 1; } /** * - * @param selection - * @param seqstring - * @param product - * - array of DbRefEntry objects from which exon map in seqstring is - * derived - * @param viscontigs - * @param gapCharacter - * @param annotations - * @param aWidth - * @param dataset * @return */ - public static AlignmentI CdnaTranslate(SequenceI[] selection, - String[] seqstring, DBRefEntry[] product, int viscontigs[], - char gapCharacter, AlignmentAnnotation[] annotations, int aWidth, - Alignment dataset) + public AlignmentI translateCdna() { - AlignedCodonFrame codons = new AlignedCodonFrame(aWidth); // stores hash of - // subsequent - // positions for - // each codon - // start position - // in alignment - int s, sSize = selection.length; - Vector pepseqs = new Vector(); + AlignedCodonFrame acf = new AlignedCodonFrame(); + + alignedCodons = new AlignedCodon[dnaWidth]; + + int s; + int sSize = selection.size(); + List pepseqs = new ArrayList(); for (s = 0; s < sSize; s++) { - SequenceI newseq = translateCodingRegion(selection[s], seqstring[s], - viscontigs, codons, gapCharacter, - (product != null) ? product[s] : null, false); // possibly - // anonymous - // product + SequenceI newseq = translateCodingRegion(selection.get(s), + seqstring[s], acf, pepseqs); + if (newseq != null) { - pepseqs.addElement(newseq); + pepseqs.add(newseq); SequenceI ds = newseq; if (dataset != null) { @@ -145,15 +182,15 @@ public class Dna } } } - if (codons.aaWidth == 0) - return null; - SequenceI[] newseqs = new SequenceI[pepseqs.size()]; - pepseqs.copyInto(newseqs); + + SequenceI[] newseqs = pepseqs.toArray(new SequenceI[pepseqs.size()]); AlignmentI al = new Alignment(newseqs); - al.padGaps(); // ensure we look aligned. + // ensure we look aligned. + al.padGaps(); + // link the protein translation to the DNA dataset al.setDataset(dataset); - translateAlignedAnnotations(annotations, al, codons); - al.addCodonFrame(codons); + translateAlignedAnnotations(al, acf); + al.addCodonFrame(acf); return al; } @@ -172,14 +209,13 @@ public class Dna for (int gd = 0; gd < selection.length; gd++) { SequenceI dna = selection[gd]; - jalview.datamodel.DBRefEntry[] dnarefs = jalview.util.DBRefUtils + DBRefEntry[] dnarefs = DBRefUtils .selectRefs(dna.getDBRef(), jalview.datamodel.DBRefSource.DNACODINGDBS); if (dnarefs != null) { // intersect with pep - // intersect with pep - Vector mappedrefs = new Vector(); + List mappedrefs = new ArrayList(); DBRefEntry[] refs = dna.getDBRef(); for (int d = 0; d < refs.length; d++) { @@ -187,11 +223,10 @@ public class Dna && refs[d].getMap().getMap().getFromRatio() == 3 && refs[d].getMap().getMap().getToRatio() == 1) { - mappedrefs.addElement(refs[d]); // add translated protein maps + mappedrefs.add(refs[d]); // add translated protein maps } } - dnarefs = new DBRefEntry[mappedrefs.size()]; - mappedrefs.copyInto(dnarefs); + dnarefs = mappedrefs.toArray(new DBRefEntry[mappedrefs.size()]); for (int d = 0; d < dnarefs.length; d++) { Mapping mp = dnarefs[d].getMap(); @@ -214,176 +249,107 @@ public class Dna } /** - * generate a set of translated protein products from annotated sequenceI + * Translate nucleotide alignment annotations onto translated amino acid + * alignment using codon mapping codons * - * @param selection - * @param viscontigs - * @param gapCharacter - * @param dataset - * destination dataset for translated sequences - * @param annotations - * @param aWidth - * @return - */ - public static AlignmentI CdnaTranslate(SequenceI[] selection, - int viscontigs[], char gapCharacter, Alignment dataset) - { - int alwidth = 0; - Vector cdnasqs = new Vector(); - Vector cdnasqi = new Vector(); - Vector cdnaprod = new Vector(); - for (int gd = 0; gd < selection.length; gd++) - { - SequenceI dna = selection[gd]; - jalview.datamodel.DBRefEntry[] dnarefs = jalview.util.DBRefUtils - .selectRefs(dna.getDBRef(), - jalview.datamodel.DBRefSource.DNACODINGDBS); - if (dnarefs != null) - { - // intersect with pep - Vector mappedrefs = new Vector(); - DBRefEntry[] refs = dna.getDBRef(); - for (int d = 0; d < refs.length; d++) - { - if (refs[d].getMap() != null && refs[d].getMap().getMap() != null - && refs[d].getMap().getMap().getFromRatio() == 3 - && refs[d].getMap().getMap().getToRatio() == 1) - { - mappedrefs.addElement(refs[d]); // add translated protein maps - } - } - dnarefs = new DBRefEntry[mappedrefs.size()]; - mappedrefs.copyInto(dnarefs); - for (int d = 0; d < dnarefs.length; d++) - { - Mapping mp = dnarefs[d].getMap(); - StringBuffer sqstr = new StringBuffer(); - if (mp != null) - { - Mapping intersect = mp.intersectVisContigs(viscontigs); - // generate seqstring for this sequence based on mapping - - if (sqstr.length() > alwidth) - alwidth = sqstr.length(); - cdnasqs.addElement(sqstr.toString()); - cdnasqi.addElement(dna); - cdnaprod.addElement(intersect); - } - } - } - SequenceI[] cdna = new SequenceI[cdnasqs.size()]; - DBRefEntry[] prods = new DBRefEntry[cdnaprod.size()]; - String[] xons = new String[cdnasqs.size()]; - cdnasqs.copyInto(xons); - cdnaprod.copyInto(prods); - cdnasqi.copyInto(cdna); - return CdnaTranslate(cdna, xons, prods, viscontigs, gapCharacter, - null, alwidth, dataset); - } - return null; - } - - /** - * translate na alignment annotations onto translated amino acid alignment al - * using codon mapping codons - * - * @param annotations * @param al - * @param codons + * the translated protein alignment */ - public static void translateAlignedAnnotations( - AlignmentAnnotation[] annotations, AlignmentI al, - AlignedCodonFrame codons) + protected void translateAlignedAnnotations(AlignmentI al, + AlignedCodonFrame acf) { - // ////////////////////////////// - // Copy annotations across - // // Can only do this for columns with consecutive codons, or where // annotation is sequence associated. - int pos, a, aSize; if (annotations != null) { - for (int i = 0; i < annotations.length; i++) + for (AlignmentAnnotation annotation : annotations) { - // Skip any autogenerated annotation - if (annotations[i].autoCalculated) + /* + * Skip hidden or autogenerated annotation. Also (for now), RNA + * secondary structure annotation. If we want to show this against + * protein we need a smarter way to 'translate' without generating + * invalid (unbalanced) structure annotation. + */ + if (annotation.autoCalculated || !annotation.visible + || annotation.isRNA()) { continue; } - aSize = codons.getaaWidth(); // aa alignment width. - jalview.datamodel.Annotation[] anots = (annotations[i].annotations == null) ? null - : new jalview.datamodel.Annotation[aSize]; + int aSize = aaWidth; + Annotation[] anots = (annotation.annotations == null) ? null + : new Annotation[aSize]; if (anots != null) { - for (a = 0; a < aSize; a++) + for (int a = 0; a < aSize; a++) { // process through codon map. - if (codons.codons[a] != null - && codons.codons[a][0] == (codons.codons[a][2] - 2)) + if (a < alignedCodons.length && alignedCodons[a] != null + && alignedCodons[a].pos1 == (alignedCodons[a].pos3 - 2)) { - anots[a] = getCodonAnnotation(codons.codons[a], - annotations[i].annotations); + anots[a] = getCodonAnnotation(alignedCodons[a], + annotation.annotations); } } } - jalview.datamodel.AlignmentAnnotation aa = new jalview.datamodel.AlignmentAnnotation( - annotations[i].label, annotations[i].description, anots); - aa.graph = annotations[i].graph; - aa.graphGroup = annotations[i].graphGroup; - aa.graphHeight = annotations[i].graphHeight; - if (annotations[i].getThreshold() != null) + AlignmentAnnotation aa = new AlignmentAnnotation(annotation.label, + annotation.description, anots); + aa.graph = annotation.graph; + aa.graphGroup = annotation.graphGroup; + aa.graphHeight = annotation.graphHeight; + if (annotation.getThreshold() != null) { - aa.setThreshold(new jalview.datamodel.GraphLine(annotations[i] + aa.setThreshold(new GraphLine(annotation .getThreshold())); } - if (annotations[i].hasScore) + if (annotation.hasScore) { - aa.setScore(annotations[i].getScore()); + aa.setScore(annotation.getScore()); } - if (annotations[i].sequenceRef != null) + + final SequenceI seqRef = annotation.sequenceRef; + if (seqRef != null) { - SequenceI aaSeq = codons - .getAaForDnaSeq(annotations[i].sequenceRef); + SequenceI aaSeq = acf.getAaForDnaSeq(seqRef); if (aaSeq != null) { // aa.compactAnnotationArray(); // throw away alignment annotation // positioning aa.setSequenceRef(aaSeq); - aa.createSequenceMapping(aaSeq, aaSeq.getStart(), true); // rebuild - // mapping + // rebuild mapping + aa.createSequenceMapping(aaSeq, aaSeq.getStart(), true); aa.adjustForAlignment(); aaSeq.addAlignmentAnnotation(aa); } - } al.addAnnotation(aa); } } } - private static Annotation getCodonAnnotation(int[] is, + private static Annotation getCodonAnnotation(AlignedCodon is, Annotation[] annotations) { // Have a look at all the codon positions for annotation and put the first // one found into the translated annotation pos. int contrib = 0; Annotation annot = null; - for (int p = 0; p < 3; p++) + for (int p = 1; p <= 3; p++) { - if (annotations[is[p]] != null) + int dnaCol = is.getBaseColumn(p); + if (annotations[dnaCol] != null) { if (annot == null) { - annot = new Annotation(annotations[is[p]]); + annot = new Annotation(annotations[dnaCol]); contrib = 1; } else { // merge with last - Annotation cpy = new Annotation(annotations[is[p]]); + Annotation cpy = new Annotation(annotations[dnaCol]); if (annot.colour == null) { annot.colour = cpy.colour; @@ -407,7 +373,7 @@ public class Dna } if (contrib > 1) { - annot.value /= (float) contrib; + annot.value /= contrib; } return annot; } @@ -419,92 +385,72 @@ public class Dna * sequence displayed under viscontigs visible columns * @param seqstring * ORF read in some global alignment reference frame - * @param viscontigs - * mapping from global reference frame to visible seqstring ORF read - * @param codons - * Definition of global ORF alignment reference frame - * @param gapCharacter - * @return sequence ready to be added to alignment. - * @deprecated Use - * {@link #translateCodingRegion(SequenceI,String,int[],AlignedCodonFrame,char,DBRefEntry,boolean)} - * instead - */ - public static SequenceI translateCodingRegion(SequenceI selection, - String seqstring, int[] viscontigs, AlignedCodonFrame codons, - char gapCharacter, DBRefEntry product) - { - return translateCodingRegion(selection, seqstring, viscontigs, codons, - gapCharacter, product, false); - } - - /** - * Translate a na sequence - * - * @param selection - * sequence displayed under viscontigs visible columns - * @param seqstring - * ORF read in some global alignment reference frame - * @param viscontigs - * mapping from global reference frame to visible seqstring ORF read - * @param codons + * @param acf * Definition of global ORF alignment reference frame - * @param gapCharacter - * @param starForStop - * when true stop codons will translate as '*', otherwise as 'X' + * @param proteinSeqs * @return sequence ready to be added to alignment. */ - public static SequenceI translateCodingRegion(SequenceI selection, - String seqstring, int[] viscontigs, AlignedCodonFrame codons, - char gapCharacter, DBRefEntry product, final boolean starForStop) + protected SequenceI translateCodingRegion(SequenceI selection, + String seqstring, AlignedCodonFrame acf, + List proteinSeqs) { - java.util.List skip = new ArrayList(); + List skip = new ArrayList(); int skipint[] = null; ShiftList vismapping = new ShiftList(); // map from viscontigs to seqstring // intervals - int vc, scontigs[] = new int[viscontigs.length]; + int vc; + int[] scontigs = new int[contigs.length]; int npos = 0; - for (vc = 0; vc < viscontigs.length; vc += 2) + for (vc = 0; vc < contigs.length; vc += 2) { if (vc == 0) { - vismapping.addShift(npos, viscontigs[vc]); + vismapping.addShift(npos, contigs[vc]); } else { // hidden region - vismapping.addShift(npos, viscontigs[vc] - viscontigs[vc - 1] + 1); + vismapping.addShift(npos, contigs[vc] - contigs[vc - 1] + 1); } - scontigs[vc] = viscontigs[vc]; - scontigs[vc + 1] = viscontigs[vc + 1]; + scontigs[vc] = contigs[vc]; + scontigs[vc + 1] = contigs[vc + 1]; } - StringBuffer protein = new StringBuffer(); - String seq = seqstring.replace('U', 'T'); + // allocate a roughly sized buffer for the protein sequence + StringBuilder protein = new StringBuilder(seqstring.length() / 2); + String seq = seqstring.replace('U', 'T').replace('u', 'T'); char codon[] = new char[3]; - int cdp[] = new int[3], rf = 0, lastnpos = 0, nend; + int cdp[] = new int[3]; + int rf = 0; + int lastnpos = 0; + int nend; int aspos = 0; int resSize = 0; for (npos = 0, nend = seq.length(); npos < nend; npos++) { - if (!jalview.util.Comparison.isGap(seq.charAt(npos))) + if (!Comparison.isGap(seq.charAt(npos))) { cdp[rf] = npos; // store position codon[rf++] = seq.charAt(npos); // store base } - // filled an RF yet ? if (rf == 3) { + /* + * Filled up a reading frame... + */ + AlignedCodon alignedCodon = new AlignedCodon(cdp[0], cdp[1], cdp[2]); String aa = ResidueProperties.codonTranslate(new String(codon)); rf = 0; + final String gapString = String.valueOf(gapChar); if (aa == null) { - aa = String.valueOf(gapCharacter); + aa = gapString; if (skipint == null) { skipint = new int[] - { cdp[0], cdp[2] }; + { alignedCodon.pos1, alignedCodon.pos3 /* cdp[0], cdp[2] */}; } - skipint[1] = cdp[2]; + skipint[1] = alignedCodon.pos3; // cdp[2]; } else { @@ -599,52 +545,66 @@ public class Dna } if (aa.equals("STOP")) { - aa = starForStop ? "*" : "X"; + aa = STOP_X; } resSize++; } - // insert/delete gaps prior to this codon - if necessary boolean findpos = true; while (findpos) { - // first ensure that the codons array is long enough. - codons.checkCodonFrameWidth(aspos); - // now check to see if we place the aa at the current aspos in the - // protein alignment - switch (Dna.compare_codonpos(cdp, codons.codons[aspos])) + /* + * Compare this codon's base positions with those currently aligned to + * this column in the translation. + */ + final int compareCodonPos = compareCodonPos(alignedCodon, + alignedCodons[aspos]); + switch (compareCodonPos) { case -1: - codons.insertAAGap(aspos, gapCharacter); + + /* + * This codon should precede the mapped positions - need to insert a + * gap in all prior sequences. + */ + insertAAGap(aspos, proteinSeqs); findpos = false; break; + case +1: - // this aa appears after the aligned codons at aspos, so prefix it - // with a gap - aa = "" + gapCharacter + aa; + + /* + * This codon belongs after the aligned codons at aspos. Prefix it + * with a gap and try the next position. + */ + aa = gapString + aa; aspos++; - // if (aspos >= codons.aaWidth) - // codons.aaWidth = aspos + 1; - break; // check the next position for alignment + break; + case 0: - // codon aligns at aspos position. + + /* + * Exact match - codon 'belongs' at this translated position. + */ findpos = false; } } - // codon aligns with all other sequence residues found at aspos protein.append(aa); lastnpos = npos; - if (codons.codons[aspos] == null) + if (alignedCodons[aspos] == null) { // mark this column as aligning to this aligned reading frame - codons.codons[aspos] = new int[] - { cdp[0], cdp[1], cdp[2] }; + alignedCodons[aspos] = alignedCodon; + } + else if (!alignedCodons[aspos].equals(alignedCodon)) + { + throw new IllegalStateException("Tried to coalign " + + alignedCodons[aspos].toString() + " with " + + alignedCodon.toString()); } - if (aspos >= codons.aaWidth) + if (aspos >= aaWidth) { // update maximum alignment width - // (we can do this without calling checkCodonFrameWidth because it was - // already done above) - codons.setAaWidth(aspos); + aaWidth = aspos; } // ready for next translated reading frame alignment position (if any) aspos++; @@ -656,15 +616,14 @@ public class Dna protein.toString()); if (rf != 0) { - if (jalview.bin.Cache.log != null) + final String errMsg = "trimming contigs for incomplete terminal codon."; + if (Cache.log != null) { - jalview.bin.Cache.log - .debug("trimming contigs for incomplete terminal codon."); + Cache.log.debug(errMsg); } else { - System.err - .println("trimming contigs for incomplete terminal codon."); + System.err.println(errMsg); } // map and trim contigs to ORF region vc = scontigs.length - 1; @@ -694,7 +653,9 @@ public class Dna scontigs = t; } if (vc <= 0) + { scontigs = null; + } } if (scontigs != null) { @@ -705,7 +666,9 @@ public class Dna scontigs[vc] = selection.findPosition(scontigs[vc]); // not from 1! scontigs[vc + 1] = selection.findPosition(scontigs[vc + 1]); // exclusive if (scontigs[vc + 1] == selection.getEnd()) + { break; + } } // trim trailing empty intervals. if ((vc + 2) < scontigs.length) @@ -731,27 +694,19 @@ public class Dna MapList map = new MapList(scontigs, new int[] { 1, resSize }, 3, 1); - // update newseq as if it was generated as mapping from product - - if (product != null) - { - newseq.setName(product.getSource() + "|" - + product.getAccessionId()); - if (product.getMap() != null) - { - // Mapping mp = product.getMap(); - // newseq.setStart(mp.getPosition(scontigs[0])); - // newseq.setEnd(mp - // .getPosition(scontigs[scontigs.length - 1])); - } - } transferCodedFeatures(selection, newseq, map, null, null); - SequenceI rseq = newseq.deriveSequence(); // construct a dataset - // sequence for our new - // peptide, regardless. - // store a mapping (this actually stores a mapping between the dataset - // sequences for the two sequences - codons.addMap(selection, rseq, map); + + /* + * Construct a dataset sequence for our new peptide. + */ + SequenceI rseq = newseq.deriveSequence(); + + /* + * Store a mapping (between the dataset sequences for the two + * sequences). + */ + // SIDE-EFFECT: acf stores the aligned sequence reseq; to remove! + acf.addMap(selection, rseq, map); return rseq; } } @@ -761,6 +716,53 @@ public class Dna } /** + * Insert a gap into the aligned proteins and the codon mapping array. + * + * @param pos + * @param proteinSeqs + * @return + */ + protected void insertAAGap(int pos, + List proteinSeqs) + { + aaWidth++; + for (SequenceI seq : proteinSeqs) + { + seq.insertCharAt(pos, gapChar); + } + + checkCodonFrameWidth(); + if (pos < aaWidth) + { + aaWidth++; + + /* + * Shift from [pos] to the end one to the right, and null out [pos] + */ + System.arraycopy(alignedCodons, pos, alignedCodons, pos + 1, + alignedCodons.length - pos - 1); + alignedCodons[pos] = null; + } + } + + /** + * Check the codons array can accommodate a single insertion, if not resize + * it. + */ + protected void checkCodonFrameWidth() + { + if (alignedCodons[alignedCodons.length - 1] != null) + { + /* + * arraycopy insertion would bump a filled slot off the end, so expand. + */ + AlignedCodon[] c = new AlignedCodon[alignedCodons.length + 10]; + System.arraycopy(alignedCodons, 0, c, 0, alignedCodons.length); + alignedCodons = c; + } + } + + /** * Given a peptide newly translated from a dna sequence, copy over and set any * features on the peptide from the DNA. If featureTypes is null, all features * on the dna sequence are searched (rather than just the displayed ones), and @@ -770,20 +772,20 @@ public class Dna * @param pep * @param map * @param featureTypes - * hash who's keys are the displayed feature type strings + * hash whose keys are the displayed feature type strings * @param featureGroups * hash where keys are feature groups and values are Boolean objects * indicating if they are displayed. */ private static void transferCodedFeatures(SequenceI dna, SequenceI pep, - MapList map, Hashtable featureTypes, Hashtable featureGroups) + MapList map, Map featureTypes, + Map featureGroups) { - SequenceFeature[] sf = (dna.getDatasetSequence() != null ? dna + SequenceFeature[] sfs = (dna.getDatasetSequence() != null ? dna .getDatasetSequence() : dna).getSequenceFeatures(); Boolean fgstate; - jalview.datamodel.DBRefEntry[] dnarefs = jalview.util.DBRefUtils - .selectRefs(dna.getDBRef(), - jalview.datamodel.DBRefSource.DNACODINGDBS); + DBRefEntry[] dnarefs = DBRefUtils.selectRefs(dna.getDBRef(), + DBRefSource.DNACODINGDBS); if (dnarefs != null) { // intersect with pep @@ -795,16 +797,16 @@ public class Dna } } } - if (sf != null) + if (sfs != null) { - for (int f = 0; f < sf.length; f++) + for (SequenceFeature sf : sfs) { - fgstate = (featureGroups == null) ? null : ((Boolean) featureGroups - .get(sf[f].featureGroup)); - if ((featureTypes == null || featureTypes.containsKey(sf[f] - .getType())) && (fgstate == null || fgstate.booleanValue())) + fgstate = (featureGroups == null) ? null : featureGroups + .get(sf.featureGroup); + if ((featureTypes == null || featureTypes.containsKey(sf.getType())) + && (fgstate == null || fgstate.booleanValue())) { - if (FeatureProperties.isCodingFeature(null, sf[f].getType())) + if (FeatureProperties.isCodingFeature(null, sf.getType())) { // if (map.intersectsFrom(sf[f].begin, sf[f].end)) { diff --git a/src/jalview/api/AlignViewportI.java b/src/jalview/api/AlignViewportI.java index 2dea15e..324e6ca 100644 --- a/src/jalview/api/AlignViewportI.java +++ b/src/jalview/api/AlignViewportI.java @@ -354,4 +354,27 @@ public interface AlignViewportI boolean hasHiddenRows(); + /** + * Returns a viewport which holds the cDna for this (protein), or vice versa, + * or null if none is set. + * + * @return + */ + AlignViewportI getCodingComplement(); + + + /** + * Sets the viewport which holds the cDna for this (protein), or vice versa. + * Implementation should guarantee that the reciprocal relationship is always + * set, i.e. each viewport is the complement of the other. + */ + void setCodingComplement(AlignViewportI sl); + + /** + * Answers true if viewport hosts DNA/RNA, else false. + * + * @return + */ + boolean isNucleotide(); + } diff --git a/src/jalview/appletgui/AlignFrame.java b/src/jalview/appletgui/AlignFrame.java index 1f32da0..4805d6b 100644 --- a/src/jalview/appletgui/AlignFrame.java +++ b/src/jalview/appletgui/AlignFrame.java @@ -1692,7 +1692,6 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, int hiddenOffset = viewport.getSelectionGroup().getStartRes(); for (int[] region : viewport.getColumnSelection().getHiddenColumns()) { - copiedHiddenColumns.addElement(new int[] { region[0] - hiddenOffset, region[1] - hiddenOffset }); } diff --git a/src/jalview/appletgui/SeqPanel.java b/src/jalview/appletgui/SeqPanel.java index a95dd27..12cb4a7 100644 --- a/src/jalview/appletgui/SeqPanel.java +++ b/src/jalview/appletgui/SeqPanel.java @@ -32,6 +32,7 @@ import jalview.schemes.ResidueProperties; import jalview.structure.SelectionSource; import jalview.structure.SequenceListener; import jalview.structure.StructureSelectionManager; +import jalview.structure.VamsasSource; import jalview.util.MessageManager; import java.awt.BorderLayout; @@ -159,8 +160,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, void setCursorPosition() { - SequenceI sequence = av.getAlignment().getSequenceAt( - seqCanvas.cursorY); + SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY); seqCanvas.cursorX = sequence.findIndex(getKeyboardNo1()) - 1; scrollToVisible(); @@ -252,8 +252,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, void setSelectionAreaAtCursor(boolean topLeft) { - SequenceI sequence = av.getAlignment().getSequenceAt( - seqCanvas.cursorY); + SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY); if (av.getSelectionGroup() != null) { @@ -680,6 +679,12 @@ public class SeqPanel extends Panel implements MouseMotionListener, } + @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"); @@ -716,39 +721,38 @@ public class SeqPanel extends Panel implements MouseMotionListener, mouseOverSequence(sequence, res, respos); } - StringBuffer text = new StringBuffer("Sequence " + (seq + 1) + " ID: " - + sequence.getName()); + StringBuilder text = new StringBuilder(); + text.append("Sequence ").append(Integer.toString(seq + 1)) + .append(" ID: ").append(sequence.getName()); - Object obj = null; + String obj = null; + final String ch = String.valueOf(sequence.getCharAt(res)); if (av.getAlignment().isNucleotide()) { - obj = ResidueProperties.nucleotideName.get(sequence.getCharAt(res) - + ""); + obj = ResidueProperties.nucleotideName.get(ch); if (obj != null) { - text.append(" Nucleotide: "); + text.append(" Nucleotide: ").append(obj); } } else { - obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + ""); + obj = "X".equalsIgnoreCase(ch) ? "STOP" + : ResidueProperties.aa2Triplet.get(ch); if (obj != null) { - text.append(" Residue: "); + text.append(" Residue: ").append(obj); } } if (obj != null) { - if (obj != "") - { - text.append(obj + " (" + respos + ")"); - } + text.append(" (").append(Integer.toString(respos)).append(")"); } ap.alignFrame.statusBar.setText(text.toString()); - StringBuffer tooltipText = new StringBuffer(); + StringBuilder tooltipText = new StringBuilder(); SequenceGroup[] groups = av.getAlignment().findAllGroups(sequence); if (groups != null) { @@ -759,7 +763,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, if (!groups[g].getName().startsWith("JTreeGroup") && !groups[g].getName().startsWith("JGroup")) { - tooltipText.append(groups[g].getName() + " "); + tooltipText.append(groups[g].getName()).append(" "); } if (groups[g].getDescription() != null) { @@ -960,15 +964,18 @@ public class SeqPanel extends Panel implements MouseMotionListener, StringBuffer message = new StringBuffer(); if (groupEditing) { - message.append(MessageManager.getString("action.edit_group")).append(":"); + message.append(MessageManager.getString("action.edit_group")).append( + ":"); if (editCommand == null) { - editCommand = new EditCommand(MessageManager.getString("action.edit_group")); + editCommand = new EditCommand( + MessageManager.getString("action.edit_group")); } } else { - message.append(MessageManager.getString("label.edit_sequence")).append(" " + seq.getName()); + message.append(MessageManager.getString("label.edit_sequence")) + .append(" " + seq.getName()); String label = seq.getName(); if (label.length() > 10) { @@ -976,7 +983,9 @@ public class SeqPanel extends Panel implements MouseMotionListener, } if (editCommand == null) { - editCommand = new EditCommand(MessageManager.formatMessage("label.edit_params", new String[]{label})); + editCommand = new EditCommand(MessageManager.formatMessage( + "label.edit_params", new String[] + { label })); } } @@ -1178,8 +1187,8 @@ public class SeqPanel extends Panel implements MouseMotionListener, } else { - editCommand.appendEdit(Action.INSERT_GAP, groupSeqs, - startres, startres - lastres, av.getAlignment(), true); + editCommand.appendEdit(Action.INSERT_GAP, groupSeqs, startres, + startres - lastres, av.getAlignment(), true); } } else @@ -1194,8 +1203,8 @@ public class SeqPanel extends Panel implements MouseMotionListener, } else { - editCommand.appendEdit(Action.DELETE_GAP, groupSeqs, - startres, lastres - startres, av.getAlignment(), true); + editCommand.appendEdit(Action.DELETE_GAP, groupSeqs, startres, + lastres - startres, av.getAlignment(), true); } } @@ -1290,16 +1299,16 @@ public class SeqPanel extends Panel implements MouseMotionListener, editCommand.appendEdit(Action.DELETE_GAP, seq, blankColumn, 1, av.getAlignment(), true); - editCommand.appendEdit(Action.INSERT_GAP, seq, j, 1, - av.getAlignment(), true); + editCommand.appendEdit(Action.INSERT_GAP, seq, j, 1, av.getAlignment(), + true); } void deleteChar(int j, SequenceI[] seq, int fixedColumn) { - editCommand.appendEdit(Action.DELETE_GAP, seq, j, 1, - av.getAlignment(), true); + editCommand.appendEdit(Action.DELETE_GAP, seq, j, 1, av.getAlignment(), + true); editCommand.appendEdit(Action.INSERT_GAP, seq, fixedColumn, 1, av.getAlignment(), true); diff --git a/src/jalview/bin/Jalview.java b/src/jalview/bin/Jalview.java index 50e3559..c7bea36 100755 --- a/src/jalview/bin/Jalview.java +++ b/src/jalview/bin/Jalview.java @@ -939,7 +939,7 @@ public class Jalview private static FeatureFetcher startFeatureFetching(final Vector dasSources) { FeatureFetcher ff = new FeatureFetcher(); - AlignFrame afs[] = Desktop.getAlignframes(); + AlignFrame afs[] = Desktop.getAlignFrames(); if (afs == null || afs.length == 0) { return null; diff --git a/src/jalview/bin/JalviewLite.java b/src/jalview/bin/JalviewLite.java index dab649e..2e9e25c 100644 --- a/src/jalview/bin/JalviewLite.java +++ b/src/jalview/bin/JalviewLite.java @@ -60,6 +60,7 @@ import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.URL; import java.util.Hashtable; +import java.util.List; import java.util.StringTokenizer; import java.util.Vector; @@ -447,12 +448,11 @@ public class JalviewLite extends Applet implements end = rs.findIndex(end); if (csel != null) { - Vector cs = csel.getSelected(); + List cs = csel.getSelected(); csel.clear(); - for (int csi = 0, csiS = cs.size(); csi < csiS; csi++) + for (Integer selectedCol : cs) { - csel.addElement(rs.findIndex(((Integer) cs.elementAt(csi)) - .intValue())); + csel.addElement(rs.findIndex(selectedCol)); } } } diff --git a/src/jalview/commands/EditCommand.java b/src/jalview/commands/EditCommand.java index 82de3b2..38a45ce 100644 --- a/src/jalview/commands/EditCommand.java +++ b/src/jalview/commands/EditCommand.java @@ -26,11 +26,16 @@ import jalview.datamodel.Annotation; 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; /** * @@ -58,7 +63,55 @@ public class EditCommand implements CommandI { public enum Action { - INSERT_GAP, DELETE_GAP, CUT, PASTE, REPLACE, INSERT_NUC + INSERT_GAP() + { + @Override + public Action getUndoAction() + { + return DELETE_GAP; + } + }, + DELETE_GAP() + { + @Override + public Action getUndoAction() + { + return INSERT_GAP; + } + }, + CUT() + { + @Override + public Action getUndoAction() + { + return PASTE; + } + }, + PASTE() + { + @Override + public Action getUndoAction() + { + return CUT; + } + }, + REPLACE + { + @Override + public Action getUndoAction() + { + return REPLACE; + } + }, + INSERT_NUC + { + @Override + public Action getUndoAction() + { + return null; + } + }; + public abstract Action getUndoAction(); }; private List edits = new ArrayList(); @@ -110,13 +163,88 @@ public class EditCommand implements CommandI } /** - * 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 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 + *
    + *
  • a new Insert positions to the right of the last , or
  • + *
  • a new Delete gaps which is positions to the left of the last + * delete.
  • + *
+ */ + 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; } /** @@ -209,7 +337,32 @@ public class EditCommand implements CommandI 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) { @@ -223,7 +376,7 @@ public class EditCommand implements CommandI * @param commandIndex * @param views */ - final void performEdit(int commandIndex, AlignmentI[] views) + public final void performEdit(int commandIndex, AlignmentI[] views) { ListIterator iterator = edits.listIterator(commandIndex); while (iterator.hasNext()) @@ -239,7 +392,7 @@ public class EditCommand implements CommandI * @param edit * @param views */ - protected void performEdit(Edit edit, AlignmentI[] views) + protected static void performEdit(Edit edit, AlignmentI[] views) { switch (edit.command) { @@ -316,13 +469,13 @@ public class EditCommand implements CommandI * * @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); } @@ -348,7 +501,7 @@ public class EditCommand implements CommandI * * @param command */ - final private void deleteGap(Edit command) + final static private void deleteGap(Edit command) { for (int s = 0; s < command.seqs.length; s++) { @@ -366,7 +519,7 @@ public class EditCommand implements CommandI * @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][]; @@ -430,7 +583,7 @@ public class EditCommand implements CommandI * @param command * @param views */ - void paste(Edit command, AlignmentI[] views) + static void paste(Edit command, AlignmentI[] views) { StringBuffer tmp; boolean newDSNeeded; @@ -446,7 +599,7 @@ public class EditCommand implements CommandI if (command.seqs[i].getLength() < 1) { // ie this sequence was deleted, we need to - // read it to the alignment + // readd it to the alignment if (command.alIndex[i] < command.al.getHeight()) { List sequences; @@ -548,7 +701,7 @@ public class EditCommand implements CommandI command.string = null; } - void replace(Edit command) + static void replace(Edit command) { StringBuffer tmp; String oldstring; @@ -625,7 +778,7 @@ public class EditCommand implements CommandI } } - final void adjustAnnotations(Edit command, boolean insert, + final static void adjustAnnotations(Edit command, boolean insert, boolean modifyVisibility, AlignmentI[] views) { AlignmentAnnotation[] annotations = null; @@ -949,7 +1102,7 @@ public class EditCommand implements CommandI } } - 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]; @@ -1027,7 +1180,112 @@ public class EditCommand implements CommandI } - class Edit + /** + * Returns the list of edit commands wrapped by this object. + * + * @return + */ + public List 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 priorState(boolean forUndo) + { + Map result = new HashMap(); + 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 edits = new ReverseListIterator(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 + { + System.err.println("Can't undo edit action " + action); + // throw new IllegalStateException("Can't undo edit action " + + // action); + } + } + } + } + return result; + } + + public class Edit { public SequenceI[] oldds; @@ -1053,7 +1311,7 @@ public class EditCommand implements CommandI 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; @@ -1099,5 +1357,49 @@ public class EditCommand implements CommandI 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 getEditIterator(boolean forwards) + { + if (forwards) + { + return getEdits().iterator(); + } + else + { + return new ReverseListIterator(getEdits()); + } } } diff --git a/src/jalview/commands/OrderCommand.java b/src/jalview/commands/OrderCommand.java index b610a54..f4010a1 100644 --- a/src/jalview/commands/OrderCommand.java +++ b/src/jalview/commands/OrderCommand.java @@ -20,19 +20,46 @@ */ package jalview.commands; -import jalview.analysis.*; -import jalview.datamodel.*; +import jalview.analysis.AlignmentSorter; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.SequenceI; +/** + * An undoable command to reorder the sequences in an alignment. + * + * @author gmcarstairs + * + */ public class OrderCommand implements CommandI { String description; + /* + * The sequence order before sorting (target order for an undo) + */ SequenceI[] seqs; + /* + * The sequence order specified by this command + */ SequenceI[] seqs2; + /* + * The alignment the command acts on + */ AlignmentI al; + /** + * Constructor given the 'undo' sequence order, and the (already) sorted + * alignment. + * + * @param description + * a text label for the 'undo' menu option + * @param seqs + * the sequence order for undo + * @param al + * the alignment as ordered by this command + */ public OrderCommand(String description, SequenceI[] seqs, AlignmentI al) { this.description = description; @@ -61,4 +88,15 @@ public class OrderCommand implements CommandI { AlignmentSorter.setOrder(al, seqs); } + + /** + * Returns the sequence order used to sort, or before sorting if undo=true. + * + * @param undo + * @return + */ + public SequenceI[] getSequenceOrder(boolean undo) + { + return undo ? seqs : seqs2; + } } diff --git a/src/jalview/datamodel/AlignedCodon.java b/src/jalview/datamodel/AlignedCodon.java new file mode 100644 index 0000000..d0e62a1 --- /dev/null +++ b/src/jalview/datamodel/AlignedCodon.java @@ -0,0 +1,71 @@ +package jalview.datamodel; + +/** + * Holds the aligned column positions (base 0) for one codon in a nucleotide + * sequence. The object is immutable once created. + * + * Example: in "G-AT-C-GA" the aligned codons are (0, 2, 3) and (5, 7, 8). + * + * @author gmcarstairs + * + */ +public final class AlignedCodon +{ + public final int pos1; + + public final int pos2; + + public final int pos3; + + public AlignedCodon(int i, int j, int k) + { + pos1 = i; + pos2 = j; + pos3 = k; + } + + /** + * Returns the column position for the given base (1, 2, 3). + * + * @param base + * @return + * @throws IllegalArgumentException + * if an argument value other than 1, 2 or 3 is supplied + */ + public int getBaseColumn(int base) + { + if (base < 1 || base > 3) + { + throw new IllegalArgumentException(Integer.toString(base)); + } + return base == 1 ? pos1 : (base == 2 ? pos2 : pos3); + } + + /** + * Two aligned codons are equal if all their base positions are the same. + */ + @Override + public boolean equals(Object o) + { + /* + * Equality with null value required for consistency with + * Dna.compareCodonPos + */ + if (o == null) + { + return true; + } + if (!(o instanceof AlignedCodon)) + { + return false; + } + AlignedCodon ac = (AlignedCodon) o; + return (pos1 == ac.pos1 && pos2 == ac.pos2 && pos3 == ac.pos3); + } + + @Override + public String toString() + { + return "[" + pos1 + ", " + pos2 + ", " + pos3 + "]"; + } +} diff --git a/src/jalview/datamodel/AlignedCodonFrame.java b/src/jalview/datamodel/AlignedCodonFrame.java index 3fc08d1..d3f6ad5 100644 --- a/src/jalview/datamodel/AlignedCodonFrame.java +++ b/src/jalview/datamodel/AlignedCodonFrame.java @@ -20,132 +20,36 @@ */ package jalview.datamodel; -import java.util.Enumeration; -import java.util.Vector; - import jalview.util.MapList; /** * Stores mapping between the columns of a protein alignment and a DNA alignment * and a list of individual codon to amino acid mappings between sequences. */ - public class AlignedCodonFrame { - /** - * array of nucleotide positions for aligned codons at column of aligned - * proteins. + + /* + * tied array of na Sequence objects. */ - public int[][] codons = null; + private SequenceI[] dnaSeqs = null; - /** - * width of protein sequence alignement implicit assertion that codons.length - * >= aaWidth + /* + * tied array of Mappings to protein sequence Objects and SequenceI[] + * aaSeqs=null; MapLists where each maps from the corresponding dnaSeqs + * element to corresponding aaSeqs element */ - public int aaWidth = 0; + private Mapping[] dnaToProt = null; /** * initialise codon frame with a nominal alignment width * * @param aWidth */ - public AlignedCodonFrame(int aWidth) - { - if (aWidth <= 0) - { - codons = null; - return; - } - codons = new int[aWidth][]; - for (int res = 0; res < aWidth; res++) - codons[res] = null; - } - - /** - * ensure that codons array is at least as wide as aslen residues - * - * @param aslen - * @return (possibly newly expanded) codon array - */ - public int[][] checkCodonFrameWidth(int aslen) - { - if (codons.length <= aslen + 1) - { - // probably never have to do this ? - int[][] c = new int[codons.length + 10][]; - for (int i = 0; i < codons.length; i++) - { - c[i] = codons[i]; - codons[i] = null; - } - codons = c; - } - return codons; - } - - /** - * @return width of aligned translated amino acid residues - */ - public int getaaWidth() - { - return aaWidth; - } - - /** - * TODO: not an ideal solution - we reference the aligned amino acid sequences - * in order to make insertions on them Better would be dnaAlignment and - * aaAlignment reference.... - */ - Vector a_aaSeqs = new Vector(); - - /** - * increase aaWidth by one and insert a new aligned codon position space at - * aspos. - * - * @param aspos - */ - public void insertAAGap(int aspos, char gapCharacter) + public AlignedCodonFrame() { - // this aa appears before the aligned codons at aspos - so shift them in - // each pair of mapped sequences - aaWidth++; - if (a_aaSeqs != null) - { - // we actually have to modify the aligned sequences here, so use the - // a_aaSeqs vector - Enumeration sq = a_aaSeqs.elements(); - while (sq.hasMoreElements()) - { - ((SequenceI) sq.nextElement()).insertCharAt(aspos, gapCharacter); - } - } - checkCodonFrameWidth(aspos); - if (aspos < aaWidth) - { - aaWidth++; - System.arraycopy(codons, aspos, codons, aspos + 1, codons.length - - aspos - 1); - codons[aspos] = null; // clear so new codon position can be marked. - } } - public void setAaWidth(int aapos) - { - aaWidth = aapos; - } - - /** - * tied array of na Sequence objects. - */ - SequenceI[] dnaSeqs = null; - - /** - * tied array of Mappings to protein sequence Objects and SequenceI[] - * aaSeqs=null; MapLists where eac maps from the corresponding dnaSeqs element - * to corresponding aaSeqs element - */ - Mapping[] dnaToProt = null; - /** * add a mapping between the dataset sequences for the associated dna and * protein sequence objects @@ -179,7 +83,6 @@ public class AlignedCodonFrame // aaseq.transferAnnotation(dnaseq, new Mapping(map.getInverse())); mp.to = (aaseq.getDatasetSequence() == null) ? aaseq : aaseq .getDatasetSequence(); - a_aaSeqs.addElement(aaseq); dnaToProt[nlen] = mp; } @@ -191,7 +94,9 @@ public class AlignedCodonFrame public SequenceI[] getAaSeqs() { if (dnaToProt == null) + { return null; + } SequenceI[] sqs = new SequenceI[dnaToProt.length]; for (int sz = 0; sz < dnaToProt.length; sz++) { @@ -203,7 +108,9 @@ public class AlignedCodonFrame public MapList[] getdnaToProt() { if (dnaToProt == null) + { return null; + } MapList[] sqs = new MapList[dnaToProt.length]; for (int sz = 0; sz < dnaToProt.length; sz++) { @@ -218,9 +125,11 @@ public class AlignedCodonFrame } /** + * Return the corresponding aligned or dataset aa sequence for given dna + * sequence, null if not found. * * @param sequenceRef - * @return null or corresponding aaSeq entry for dnaSeq entry + * @return */ public SequenceI getAaForDnaSeq(SequenceI dnaSeqRef) { @@ -232,7 +141,9 @@ public class AlignedCodonFrame for (int ds = 0; ds < dnaSeqs.length; ds++) { if (dnaSeqs[ds] == dnaSeqRef || dnaSeqs[ds] == dnads) + { return dnaToProt[ds].to; + } } return null; } @@ -252,7 +163,9 @@ public class AlignedCodonFrame for (int as = 0; as < dnaToProt.length; as++) { if (dnaToProt[as].to == aaSeqRef || dnaToProt[as].to == aads) + { return dnaSeqs[as]; + } } return null; } @@ -319,4 +232,124 @@ public class AlignedCodonFrame } } } + + /** + * Returns the DNA codon positions (base 1) for the given position (base 1) in + * a mapped protein sequence, or null if no mapping is found. + * + * Intended for use in aligning cDNA to match aligned protein. Only the first + * mapping found is returned, so not suitable for use if multiple protein + * sequences are mapped to the same cDNA (but aligning cDNA as protein is + * ill-defined for this case anyway). + * + * @param seq + * the DNA dataset sequence + * @param aaPos + * residue position (base 1) in a protein sequence + * @return + */ + public int[] getDnaPosition(SequenceI seq, int aaPos) + { + /* + * Adapted from markMappedRegion(). + */ + MapList ml = null; + for (int i = 0; i < dnaToProt.length; i++) + { + if (dnaSeqs[i] == seq) + { + ml = getdnaToProt()[i]; + break; + } + } + return ml == null ? null : ml.locateInFrom(aaPos, aaPos); + } + + /** + * Convenience method to return the first aligned sequence in the given + * alignment whose dataset has a mapping with the given dataset sequence. + * + * @param seq + * + * @param al + * @return + */ + public SequenceI findAlignedSequence(SequenceI seq, AlignmentI al) + { + /* + * Search mapped protein ('to') sequences first. + */ + if (this.dnaToProt != null) + { + for (int i = 0; i < dnaToProt.length; i++) + { + if (this.dnaSeqs[i] == seq) + { + for (SequenceI sourceAligned : al.getSequences()) + { + if (this.dnaToProt[i].to == sourceAligned.getDatasetSequence()) + { + return sourceAligned; + } + } + } + } + } + + /* + * Then try mapped dna sequences. + */ + if (this.dnaToProt != null) + { + for (int i = 0; i < dnaToProt.length; i++) + { + if (this.dnaToProt[i].to == seq) + { + for (SequenceI sourceAligned : al.getSequences()) + { + if (this.dnaSeqs[i] == sourceAligned.getDatasetSequence()) + { + return sourceAligned; + } + } + } + } + } + + return null; + } + + /** + * Returns the region in the 'mappedFrom' sequence's dataset that is mapped to + * position 'pos' (base 1) in the 'mappedTo' sequence's dataset. The region is + * a set of start/end position pairs. + * + * @param mappedFrom + * @param mappedTo + * @param pos + * @return + */ + public int[] getMappedRegion(SequenceI mappedFrom, SequenceI mappedTo, + int pos) + { + SequenceI targetDs = mappedFrom.getDatasetSequence() == null ? mappedFrom + : mappedFrom.getDatasetSequence(); + SequenceI sourceDs = mappedTo.getDatasetSequence() == null ? mappedTo + : mappedTo.getDatasetSequence(); + if (targetDs == null || sourceDs == null || dnaToProt == null) + { + return null; + } + for (int mi = 0; mi < dnaToProt.length; mi++) + { + if (dnaSeqs[mi] == targetDs && dnaToProt[mi].to == sourceDs) + { + int[] codon = dnaToProt[mi].map.locateInFrom(pos, pos); + if (codon != null) { + return codon; + } + } + } + return null; + } } diff --git a/src/jalview/datamodel/Alignment.java b/src/jalview/datamodel/Alignment.java index 01d3d8d..cb571ac 100755 --- a/src/jalview/datamodel/Alignment.java +++ b/src/jalview/datamodel/Alignment.java @@ -20,13 +20,18 @@ */ package jalview.datamodel; +import jalview.analysis.AlignmentUtils; +import jalview.io.FastaFile; import jalview.util.MessageManager; import java.util.ArrayList; import java.util.Enumeration; +import java.util.HashSet; import java.util.Hashtable; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.Vector; /** @@ -62,6 +67,8 @@ public class Alignment implements AlignmentI public Hashtable alignmentProperties; + private Set codonFrameList = new LinkedHashSet(); + private void initAlignment(SequenceI[] seqs) { int i = 0; @@ -86,6 +93,27 @@ public class Alignment implements AlignmentI } /** + * Make a 'copy' alignment - sequences have new copies of features and + * annotations, but share the original dataset sequences. + */ + public Alignment(AlignmentI al) + { + SequenceI[] seqs = al.getSequencesArray(); + for (int i = 0; i < seqs.length; i++) + { + seqs[i] = new Sequence(seqs[i]); + } + + /* + * Share the same dataset sequence mappings (if any). TODO: find a better + * place for these to live (alignment dataset?). + */ + this.codonFrameList = ((Alignment) al).codonFrameList; + + initAlignment(seqs); + } + + /** * Make an alignment from an array of Sequences. * * @param sequences @@ -123,11 +151,6 @@ public class Alignment implements AlignmentI // this(compactAlignment.refCigars); } - /** - * DOCUMENT ME! - * - * @return DOCUMENT ME! - */ @Override public List getSequences() { @@ -157,6 +180,17 @@ public class Alignment implements AlignmentI } /** + * Returns a map of lists of sequences keyed by sequence name. + * + * @return + */ + @Override + public Map> getSequencesByName() + { + return AlignmentUtils.getSequencesByName(this); + } + + /** * DOCUMENT ME! * * @param i @@ -226,10 +260,9 @@ public class Alignment implements AlignmentI @Override public void setSequenceAt(int i, SequenceI snew) { - SequenceI oldseq = getSequenceAt(i); - deleteSequence(i); synchronized (sequences) { + deleteSequence(i); sequences.set(i, snew); } } @@ -298,8 +331,8 @@ public class Alignment implements AlignmentI synchronized (sequences) { sequences.remove(i); + hiddenSequences.adjustHeightSequenceDeleted(i); } - hiddenSequences.adjustHeightSequenceDeleted(i); } } @@ -720,6 +753,28 @@ public class Alignment implements AlignmentI return true; } + /** + * Delete all annotations, including auto-calculated if the flag is set true. + * Returns true if at least one annotation was deleted, else false. + * + * @param includingAutoCalculated + * @return + */ + @Override + public boolean deleteAllAnnotations(boolean includingAutoCalculated) + { + boolean result = false; + for (AlignmentAnnotation alan : getAlignmentAnnotation()) + { + if (!alan.autoCalculated || includingAutoCalculated) + { + deleteAnnotation(alan); + result = true; + } + } + return result; + } + /* * (non-Javadoc) * @@ -1205,8 +1260,6 @@ public class Alignment implements AlignmentI return alignmentProperties; } - AlignedCodonFrame[] codonFrameList = null; - /* * (non-Javadoc) * @@ -1217,31 +1270,10 @@ public class Alignment implements AlignmentI @Override public void addCodonFrame(AlignedCodonFrame codons) { - if (codons == null) + if (codons != null) { - return; + codonFrameList.add(codons); } - if (codonFrameList == null) - { - codonFrameList = new AlignedCodonFrame[] - { codons }; - return; - } - AlignedCodonFrame[] t = new AlignedCodonFrame[codonFrameList.length + 1]; - System.arraycopy(codonFrameList, 0, t, 0, codonFrameList.length); - t[codonFrameList.length] = codons; - codonFrameList = t; - } - - /* - * (non-Javadoc) - * - * @see jalview.datamodel.AlignmentI#getCodonFrame(int) - */ - @Override - public AlignedCodonFrame getCodonFrame(int index) - { - return codonFrameList[index]; } /* @@ -1251,36 +1283,42 @@ public class Alignment implements AlignmentI * jalview.datamodel.AlignmentI#getCodonFrame(jalview.datamodel.SequenceI) */ @Override - public AlignedCodonFrame[] getCodonFrame(SequenceI seq) + public List getCodonFrame(SequenceI seq) { - if (seq == null || codonFrameList == null) + if (seq == null) { return null; } - Vector cframes = new Vector(); - for (int f = 0; f < codonFrameList.length; f++) + List cframes = new ArrayList(); + for (AlignedCodonFrame acf : codonFrameList) { - if (codonFrameList[f].involvesSequence(seq)) + if (acf.involvesSequence(seq)) { - cframes.addElement(codonFrameList[f]); + cframes.add(acf); } } - if (cframes.size() == 0) - { - return null; - } - AlignedCodonFrame[] cfr = new AlignedCodonFrame[cframes.size()]; - cframes.copyInto(cfr); - return cfr; + return cframes; } - /* - * (non-Javadoc) + /** + * Sets the codon frame mappings (replacing any existing mappings). + * + * @see jalview.datamodel.AlignmentI#setCodonFrames() + */ + @Override + public void setCodonFrames(Set acfs) + { + this.codonFrameList = acfs; + } + + /** + * Returns the set of codon frame mappings. Any changes to the returned set + * will affect the alignment. * * @see jalview.datamodel.AlignmentI#getCodonFrames() */ @Override - public AlignedCodonFrame[] getCodonFrames() + public Set getCodonFrames() { return codonFrameList; } @@ -1298,26 +1336,7 @@ public class Alignment implements AlignmentI { return false; } - boolean removed = false; - int i = 0, iSize = codonFrameList.length; - while (i < iSize) - { - if (codonFrameList[i] == codons) - { - removed = true; - if (i + 1 < iSize) - { - System.arraycopy(codonFrameList, i + 1, codonFrameList, i, iSize - - i - 1); - } - iSize--; - } - else - { - i++; - } - } - return removed; + return codonFrameList.remove(codons); } @Override @@ -1362,11 +1381,9 @@ public class Alignment implements AlignmentI { addAnnotation(alan[a]); } - AlignedCodonFrame[] acod = toappend.getCodonFrames(); - for (int a = 0; acod != null && a < acod.length; a++) - { - this.addCodonFrame(acod[a]); - } + + this.codonFrameList.addAll(toappend.getCodonFrames()); + List sg = toappend.getGroups(); if (sg != null) { @@ -1627,4 +1644,94 @@ public class Alignment implements AlignmentI { return dataset; } + + /** + * Align this alignment like the given (mapped) one. + */ + @Override + public int alignAs(AlignmentI al) + { + /* + * Currently retains unmapped gaps (in introns), regaps mapped regions + * (exons) + */ + return alignAs(al, false, true); + } + + /** + * Align this alignment 'the same as' the given one. Mapped sequences only are + * realigned. If both of the same type (nucleotide/protein) then align both + * identically. If this is nucleotide and the other is protein, make 3 gaps + * for each gap in the protein sequences. If this is protein and the other is + * nucleotide, insert a gap for each 3 gaps (or part thereof) between + * nucleotide bases. Does nothing if alignment of protein from cDNA is + * requested (not yet implemented). + * + * Parameters control whether gaps in exon (mapped) and intron (unmapped) + * regions are preserved. Gaps that connect introns to exons are treated + * conservatively, i.e. only preserved if both intron and exon gaps are + * preserved. + * + * @param al + * @param preserveMappedGaps + * if true, gaps within and between mapped codons are preserved + * @param preserveUnmappedGaps + * if true, gaps within and between unmapped codons are preserved + */ +// @Override + public int alignAs(AlignmentI al, boolean preserveMappedGaps, + boolean preserveUnmappedGaps) + { + // TODO should this method signature be the one in the interface? + int count = 0; + boolean thisIsNucleotide = this.isNucleotide(); + boolean thatIsProtein = !al.isNucleotide(); + if (!thatIsProtein && !thisIsNucleotide) + { + System.err + .println("Alignment of protein from cDNA not yet implemented"); + return 0; + // todo: build it - a variant of Dna.CdnaTranslate() + } + + char thisGapChar = this.getGapCharacter(); + String gap = thisIsNucleotide && thatIsProtein ? String + .valueOf(new char[] + { thisGapChar, thisGapChar, thisGapChar }) : String + .valueOf(thisGapChar); + + /* + * Get mappings from 'that' alignment's sequences to this. + */ + for (SequenceI alignTo : getSequences()) + { + count += AlignmentUtils.alignSequenceAs(alignTo, al, gap, preserveMappedGaps, + preserveUnmappedGaps) ? 1 : 0; + } + return count; + } + + /** + * Returns the alignment in Fasta format. Behaviour of this method is not + * guaranteed between versions. + */ + @Override + public String toString() + { + return new FastaFile().print(getSequencesArray()); + } + + /** + * Returns the set of distinct sequence names. No ordering is guaranteed. + */ + @Override + public Set getSequenceNames() + { + Set names = new HashSet(); + for (SequenceI seq : getSequences()) + { + names.add(seq.getName()); + } + return names; + } } diff --git a/src/jalview/datamodel/AlignmentAnnotation.java b/src/jalview/datamodel/AlignmentAnnotation.java index e137225..0d99155 100755 --- a/src/jalview/datamodel/AlignmentAnnotation.java +++ b/src/jalview/datamodel/AlignmentAnnotation.java @@ -27,9 +27,8 @@ import jalview.analysis.WUSSParseException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Enumeration; import java.util.HashMap; -import java.util.Hashtable; +import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; @@ -155,7 +154,7 @@ public class AlignmentAnnotation /** * map of positions in the associated annotation */ - public java.util.Hashtable sequenceMapping; + private Map sequenceMapping; /** DOCUMENT ME!! */ public float graphMin; @@ -499,6 +498,7 @@ public class AlignmentAnnotation : annotations[index + offset].secondaryStructure); } + @Override public String toString() { char[] string = new char[max - offset]; @@ -710,12 +710,13 @@ public class AlignmentAnnotation if (annotation.sequenceMapping != null) { Integer p = null; - sequenceMapping = new Hashtable(); - Enumeration pos = annotation.sequenceMapping.keys(); - while (pos.hasMoreElements()) + sequenceMapping = new HashMap(); + Iterator pos = annotation.sequenceMapping.keySet() + .iterator(); + while (pos.hasNext()) { // could optimise this! - p = (Integer) pos.nextElement(); + p = pos.next(); Annotation a = annotation.sequenceMapping.get(p); if (a == null) { @@ -789,11 +790,11 @@ public class AlignmentAnnotation int epos = sequenceRef.findPosition(endRes); if (sequenceMapping != null) { - Hashtable newmapping = new Hashtable(); - Enumeration e = sequenceMapping.keys(); - while (e.hasMoreElements()) + Map newmapping = new HashMap(); + Iterator e = sequenceMapping.keySet().iterator(); + while (e.hasNext()) { - Integer pos = (Integer) e.nextElement(); + Integer pos = e.next(); if (pos.intValue() >= spos && pos.intValue() <= epos) { newmapping.put(pos, sequenceMapping.get(pos)); @@ -836,9 +837,10 @@ public class AlignmentAnnotation * * @return DOCUMENT ME! */ + @Override public String toString() { - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(256); for (int i = 0; i < annotations.length; i++) { @@ -911,7 +913,7 @@ public class AlignmentAnnotation { return; } - sequenceMapping = new java.util.Hashtable(); + sequenceMapping = new HashMap(); int seqPos; @@ -1223,7 +1225,7 @@ public class AlignmentAnnotation .getTo() == sq.getDatasetSequence()) : false; // TODO build a better annotation element map and get rid of annotations[] - Hashtable mapForsq = new Hashtable(); + Map mapForsq = new HashMap(); if (sequenceMapping != null) { if (sp2sq != null) @@ -1275,7 +1277,8 @@ public class AlignmentAnnotation { if (mapping != null) { - Hashtable old = sequenceMapping, remap = new Hashtable(); + Map old = sequenceMapping; + Map remap = new HashMap(); int index = -1; for (int mp[] : mapping) { diff --git a/src/jalview/datamodel/AlignmentI.java b/src/jalview/datamodel/AlignmentI.java index c7e30a4..fe93683 100755 --- a/src/jalview/datamodel/AlignmentI.java +++ b/src/jalview/datamodel/AlignmentI.java @@ -5,16 +5,16 @@ * This file is part of Jalview. * * Jalview is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License + * modify it under the terms of the GNU General License * as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * Jalview is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. + * PURPOSE. See the GNU General License for more details. * - * You should have received a copy of the GNU General Public License + * You should have received a copy of the GNU General License * along with Jalview. If not, see . * The Jalview Authors are detailed in the 'AUTHORS' file. */ @@ -23,6 +23,7 @@ package jalview.datamodel; import java.util.Hashtable; import java.util.List; import java.util.Map; +import java.util.Set; /** * Data structure to hold and manipulate a multiple sequence alignment @@ -34,15 +35,16 @@ public interface AlignmentI extends AnnotatedCollectionI * * @return Number of sequences in alignment */ - public int getHeight(); + int getHeight(); /** + * * Calculates the maximum width of the alignment, including gaps. * * @return Greatest sequence length within alignment. */ @Override - public int getWidth(); + int getWidth(); /** * Calculates if this set of sequences (visible and invisible) are all the @@ -50,7 +52,7 @@ public interface AlignmentI extends AnnotatedCollectionI * * @return true if all sequences in alignment are the same length */ - public boolean isAligned(); + boolean isAligned(); /** * Calculates if this set of sequences is all the same length @@ -59,7 +61,7 @@ public interface AlignmentI extends AnnotatedCollectionI * optionally exclude hidden sequences from test * @return true if all (or just visible) sequences are the same length */ - public boolean isAligned(boolean includeHidden); + boolean isAligned(boolean includeHidden); /** * Gets sequences as a Synchronized collection @@ -67,14 +69,14 @@ public interface AlignmentI extends AnnotatedCollectionI * @return All sequences in alignment. */ @Override - public List getSequences(); + List getSequences(); /** * Gets sequences as a SequenceI[] * * @return All sequences in alignment. */ - public SequenceI[] getSequencesArray(); + SequenceI[] getSequencesArray(); /** * Find a specific sequence in this alignment. @@ -84,7 +86,14 @@ public interface AlignmentI extends AnnotatedCollectionI * * @return SequenceI at given index. */ - public SequenceI getSequenceAt(int i); + SequenceI getSequenceAt(int i); + + /** + * Returns a map of lists of sequences keyed by sequence name. + * + * @return + */ + Map> getSequencesByName(); /** * Add a new sequence to this alignment. @@ -92,7 +101,7 @@ public interface AlignmentI extends AnnotatedCollectionI * @param seq * New sequence will be added at end of alignment. */ - public void addSequence(SequenceI seq); + void addSequence(SequenceI seq); /** * Used to set a particular index of the alignment with the given sequence. @@ -102,7 +111,7 @@ public interface AlignmentI extends AnnotatedCollectionI * @param seq * New sequence to be inserted. */ - public void setSequenceAt(int i, SequenceI seq); + void setSequenceAt(int i, SequenceI seq); /** * Deletes a sequence from the alignment @@ -110,7 +119,7 @@ public interface AlignmentI extends AnnotatedCollectionI * @param s * Sequence to be deleted. */ - public void deleteSequence(SequenceI s); + void deleteSequence(SequenceI s); /** * Deletes a sequence from the alignment. @@ -118,7 +127,7 @@ public interface AlignmentI extends AnnotatedCollectionI * @param i * Index of sequence to be deleted. */ - public void deleteSequence(int i); + void deleteSequence(int i); /** * Finds sequence in alignment using sequence name as query. @@ -128,9 +137,9 @@ public interface AlignmentI extends AnnotatedCollectionI * * @return Sequence matching query, if found. If not found returns null. */ - public SequenceI findName(String name); + SequenceI findName(String name); - public SequenceI[] findSequenceMatch(String name); + SequenceI[] findSequenceMatch(String name); /** * Finds index of a given sequence in the alignment. @@ -140,7 +149,7 @@ public interface AlignmentI extends AnnotatedCollectionI * * @return Index of sequence within the alignment or -1 if not found */ - public int findIndex(SequenceI s); + int findIndex(SequenceI s); /** * Finds group that given sequence is part of. @@ -151,7 +160,7 @@ public interface AlignmentI extends AnnotatedCollectionI * @return First group found for sequence. WARNING : Sequences may be members * of several groups. This method is incomplete. */ - public SequenceGroup findGroup(SequenceI s); + SequenceGroup findGroup(SequenceI s); /** * Finds all groups that a given sequence is part of. @@ -161,7 +170,7 @@ public interface AlignmentI extends AnnotatedCollectionI * * @return All groups containing given sequence. */ - public SequenceGroup[] findAllGroups(SequenceI s); + SequenceGroup[] findAllGroups(SequenceI s); /** * Adds a new SequenceGroup to this alignment. @@ -169,7 +178,7 @@ public interface AlignmentI extends AnnotatedCollectionI * @param sg * New group to be added. */ - public void addGroup(SequenceGroup sg); + void addGroup(SequenceGroup sg); /** * Deletes a specific SequenceGroup @@ -177,19 +186,19 @@ public interface AlignmentI extends AnnotatedCollectionI * @param g * Group will be deleted from alignment. */ - public void deleteGroup(SequenceGroup g); + void deleteGroup(SequenceGroup g); /** * Get all the groups associated with this alignment. * * @return All groups as a list. */ - public List getGroups(); + List getGroups(); /** * Deletes all groups from this alignment. */ - public void deleteAllGroups(); + void deleteAllGroups(); /** * Adds a new AlignmentAnnotation to this alignment @@ -197,7 +206,7 @@ public interface AlignmentI extends AnnotatedCollectionI * @note Care should be taken to ensure that annotation is at least as wide as * the longest sequence in the alignment for rendering purposes. */ - public void addAnnotation(AlignmentAnnotation aa); + void addAnnotation(AlignmentAnnotation aa); /** * moves annotation to a specified index in alignment annotation display stack @@ -207,7 +216,16 @@ public interface AlignmentI extends AnnotatedCollectionI * @param index * the destination position */ - public void setAnnotationIndex(AlignmentAnnotation aa, int index); + void setAnnotationIndex(AlignmentAnnotation aa, int index); + + /** + * Delete all annotations, including auto-calculated if the flag is set true. + * Returns true if at least one annotation was deleted, else false. + * + * @param includingAutoCalculated + * @return + */ + boolean deleteAllAnnotations(boolean includingAutoCalculated); /** * Deletes a specific AlignmentAnnotation from the alignment, and removes its @@ -219,7 +237,7 @@ public interface AlignmentI extends AnnotatedCollectionI * the annotation to delete * @return true if annotation was deleted from this alignment. */ - public boolean deleteAnnotation(AlignmentAnnotation aa); + boolean deleteAnnotation(AlignmentAnnotation aa); /** * Deletes a specific AlignmentAnnotation from the alignment, and optionally @@ -235,7 +253,7 @@ public interface AlignmentI extends AnnotatedCollectionI * into the alignment * @return true if annotation was deleted from this alignment. */ - public boolean deleteAnnotation(AlignmentAnnotation aa, boolean unhook); + boolean deleteAnnotation(AlignmentAnnotation aa, boolean unhook); /** * Get the annotation associated with this alignment (this can be null if no @@ -244,7 +262,7 @@ public interface AlignmentI extends AnnotatedCollectionI * @return array of AlignmentAnnotation objects */ @Override - public AlignmentAnnotation[] getAlignmentAnnotation(); + AlignmentAnnotation[] getAlignmentAnnotation(); /** * Change the gap character used in this alignment to 'gc' @@ -252,34 +270,34 @@ public interface AlignmentI extends AnnotatedCollectionI * @param gc * the new gap character. */ - public void setGapCharacter(char gc); + void setGapCharacter(char gc); /** * Get the gap character used in this alignment * * @return gap character */ - public char getGapCharacter(); + char getGapCharacter(); /** * Test for all nucleotide alignment * * @return true if alignment is nucleotide sequence */ - public boolean isNucleotide(); + boolean isNucleotide(); /** * Test if alignment contains RNA structure * * @return true if RNA structure AligmnentAnnotation was added to alignment */ - public boolean hasRNAStructure(); + boolean hasRNAStructure(); /** * Set alignment to be a nucleotide sequence * */ - public void setNucleotide(boolean b); + void setNucleotide(boolean b); /** * Get the associated dataset for the alignment. @@ -287,7 +305,7 @@ public interface AlignmentI extends AnnotatedCollectionI * @return Alignment containing dataset sequences or null of this is a * dataset. */ - public Alignment getDataset(); + Alignment getDataset(); /** * Set the associated dataset for the alignment, or create one. @@ -295,23 +313,23 @@ public interface AlignmentI extends AnnotatedCollectionI * @param dataset * The dataset alignment or null to construct one. */ - public void setDataset(Alignment dataset); + void setDataset(Alignment dataset); /** * pads sequences with gaps (to ensure the set looks like an alignment) * * @return boolean true if alignment was modified */ - public boolean padGaps(); + boolean padGaps(); - public HiddenSequences getHiddenSequences(); + HiddenSequences getHiddenSequences(); /** * Compact representation of alignment * * @return CigarArray */ - public CigarArray getCompactAlignment(); + CigarArray getCompactAlignment(); /** * Set an arbitrary key value pair for an alignment. Note: both key and value @@ -320,7 +338,7 @@ public interface AlignmentI extends AnnotatedCollectionI * @param key * @param value */ - public void setProperty(Object key, Object value); + void setProperty(Object key, Object value); /** * Get a named property from the alignment. @@ -328,21 +346,21 @@ public interface AlignmentI extends AnnotatedCollectionI * @param key * @return value of property */ - public Object getProperty(Object key); + Object getProperty(Object key); /** * Get the property hashtable. * * @return hashtable of alignment properties (or null if none are defined) */ - public Hashtable getProperties(); + Hashtable getProperties(); /** * add a reference to a frame of aligned codons for this alignment * * @param codons */ - public void addCodonFrame(AlignedCodonFrame codons); + void addCodonFrame(AlignedCodonFrame codons); /** * remove a particular codon frame reference from this alignment @@ -350,27 +368,24 @@ public interface AlignmentI extends AnnotatedCollectionI * @param codons * @return true if codon frame was removed. */ - public boolean removeCodonFrame(AlignedCodonFrame codons); + boolean removeCodonFrame(AlignedCodonFrame codons); /** * get all codon frames associated with this alignment * * @return */ - public AlignedCodonFrame[] getCodonFrames(); + Set getCodonFrames(); /** - * get a particular codon frame - * - * @param index - * @return + * Set the codon frame mappings (replacing any existing set). */ - public AlignedCodonFrame getCodonFrame(int index); + void setCodonFrames(Set acfs); /** * get codon frames involving sequenceI */ - public AlignedCodonFrame[] getCodonFrame(SequenceI seq); + List getCodonFrame(SequenceI seq); /** * find sequence with given name in alignment @@ -382,7 +397,7 @@ public interface AlignmentI extends AnnotatedCollectionI * tried * @return matched sequence or null */ - public SequenceI findName(String token, boolean b); + SequenceI findName(String token, boolean b); /** * find next sequence with given name in alignment starting after a given @@ -398,7 +413,7 @@ public interface AlignmentI extends AnnotatedCollectionI * tried * @return matched sequence or null */ - public SequenceI findName(SequenceI startAfter, String token, boolean b); + SequenceI findName(SequenceI startAfter, String token, boolean b); /** * find first sequence in alignment which is involved in the given search @@ -407,7 +422,7 @@ public interface AlignmentI extends AnnotatedCollectionI * @param results * @return -1 or index of sequence in alignment */ - public int findIndex(SearchResults results); + int findIndex(SearchResults results); /** * append sequences and annotation from another alignment object to this one. @@ -420,7 +435,7 @@ public interface AlignmentI extends AnnotatedCollectionI * @param toappend * - the alignment to be appended. */ - public void append(AlignmentI toappend); + void append(AlignmentI toappend); /** * Justify the sequences to the left or right by deleting and inserting gaps @@ -430,7 +445,7 @@ public interface AlignmentI extends AnnotatedCollectionI * true if alignment padded to right, false to justify to left * @return true if alignment was changed TODO: return undo object */ - public boolean justify(boolean right); + boolean justify(boolean right); /** * add given annotation row at given position (0 is start, -1 is end) @@ -438,7 +453,7 @@ public interface AlignmentI extends AnnotatedCollectionI * @param consensus * @param i */ - public void addAnnotation(AlignmentAnnotation consensus, int i); + void addAnnotation(AlignmentAnnotation consensus, int i); /** * search for or create a specific annotation row on the alignment @@ -458,7 +473,7 @@ public interface AlignmentI extends AnnotatedCollectionI * * @return existing annotation matching the given attributes */ - public AlignmentAnnotation findOrCreateAnnotation(String name, + AlignmentAnnotation findOrCreateAnnotation(String name, String calcId, boolean autoCalc, SequenceI seqRef, SequenceGroup groupRef); @@ -472,7 +487,7 @@ public interface AlignmentI extends AnnotatedCollectionI * @param up * @param i */ - public void moveSelectedSequencesByOne(SequenceGroup sg, + void moveSelectedSequencesByOne(SequenceGroup sg, Map map, boolean up); /** @@ -481,5 +496,25 @@ public interface AlignmentI extends AnnotatedCollectionI * * @param alignmentAnnotation */ - public void validateAnnotation(AlignmentAnnotation alignmentAnnotation); + void validateAnnotation(AlignmentAnnotation alignmentAnnotation); + + /** + * Align this alignment the same as the given one. If both of the same type + * (nucleotide/protein) then align both identically. If this is nucleotide and + * the other is protein, make 3 gaps for each gap in the protein sequences. If + * this is protein and the other is nucleotide, insert a gap for each 3 gaps + * (or part thereof) between nucleotide bases. Returns the number of mapped + * sequences that were realigned . + * + * @param al + * @return + */ + int alignAs(AlignmentI al); + + /** + * Returns the set of distinct sequence names in the alignment. + * + * @return + */ + Set getSequenceNames(); } diff --git a/src/jalview/datamodel/AlignmentOrder.java b/src/jalview/datamodel/AlignmentOrder.java index c0f9ab4..92d5f6c 100755 --- a/src/jalview/datamodel/AlignmentOrder.java +++ b/src/jalview/datamodel/AlignmentOrder.java @@ -20,7 +20,9 @@ */ package jalview.datamodel; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; public class AlignmentOrder { @@ -51,7 +53,7 @@ public class AlignmentOrder private String Name; - private Vector Order = null; + private List Order = null; /** * Creates a new AlignmentOrder object. @@ -64,9 +66,8 @@ public class AlignmentOrder * AlignmentOrder * * @param anOrder - * Vector */ - public AlignmentOrder(Vector anOrder) + public AlignmentOrder(List anOrder) { Order = anOrder; } @@ -79,11 +80,11 @@ public class AlignmentOrder */ public AlignmentOrder(AlignmentI orderFrom) { - Order = new Vector(); + Order = new ArrayList(); - for (int i = 0, ns = orderFrom.getHeight(); i < ns; i++) + for (SequenceI seq : orderFrom.getSequences()) { - Order.addElement(orderFrom.getSequenceAt(i)); + Order.add(seq); } } @@ -95,12 +96,7 @@ public class AlignmentOrder */ public AlignmentOrder(SequenceI[] orderFrom) { - Order = new Vector(); - - for (int i = 0, ns = orderFrom.length; i < ns; i++) - { - Order.addElement(orderFrom[i]); - } + Order = new ArrayList(Arrays.asList(orderFrom)); } /** @@ -151,7 +147,7 @@ public class AlignmentOrder * @param Order * DOCUMENT ME! */ - public void setOrder(Vector Order) + public void setOrder(List Order) { this.Order = Order; } @@ -161,7 +157,7 @@ public class AlignmentOrder * * @return DOCUMENT ME! */ - public Vector getOrder() + public List getOrder() { return Order; } @@ -178,7 +174,7 @@ public class AlignmentOrder int found = Order.indexOf(oldref); if (found > -1) { - Order.setElementAt(newref, found); + Order.set(found, newref); } return found > -1; } @@ -189,9 +185,14 @@ public class AlignmentOrder * @param o * @return true if o orders the same sequenceI objects in the same way */ - public boolean equals(AlignmentOrder o) + @Override + public boolean equals(Object o) { - return equals(o, true); + if (o == null || !(o instanceof AlignmentOrder)) + { + return false; + } + return equals((AlignmentOrder) o, true); } /** @@ -223,7 +224,7 @@ public class AlignmentOrder { for (int i = 0, j = o.Order.size(); i < j; i++) { - if (Order.elementAt(i) != o.Order.elementAt(i)) + if (Order.get(i) != o.Order.get(i)) { return false; } @@ -271,7 +272,7 @@ public class AlignmentOrder } if (Order != null && o.Order != null) { - Vector c, s; + List c, s; if (o.Order.size() > Order.size()) { c = o.Order; @@ -292,7 +293,7 @@ public class AlignmentOrder int last = -1; for (int i = 0, j = s.size(); i < j; i++) { - int pos = c.indexOf(s.elementAt(i)); // JBPNote - optimize by + int pos = c.indexOf(s.get(i)); // JBPNote - optimize by // incremental position search if (pos > last) { diff --git a/src/jalview/datamodel/ColumnSelection.java b/src/jalview/datamodel/ColumnSelection.java index 6acca32..c661f37 100644 --- a/src/jalview/datamodel/ColumnSelection.java +++ b/src/jalview/datamodel/ColumnSelection.java @@ -25,7 +25,7 @@ import jalview.viewmodel.annotationfilter.AnnotationFilterParameter; import jalview.viewmodel.annotationfilter.AnnotationFilterParameter.SearchableAnnotationField; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.Vector; @@ -303,13 +303,13 @@ public class ColumnSelection { if (shiftrecord != null) { - Vector shifts = shiftrecord.shifts; + List shifts = shiftrecord.getShifts(); if (shifts != null && shifts.size() > 0) { int shifted = 0; for (int i = 0, j = shifts.size(); i < j; i++) { - int[] sh = (int[]) shifts.elementAt(i); + int[] sh = shifts.get(i); // compensateForEdit(shifted+sh[0], sh[1]); compensateForDelEdits(shifted + sh[0], sh[1]); shifted -= sh[1]; @@ -324,16 +324,16 @@ public class ColumnSelection * removes intersection of position,length ranges in deletions from the * start,end regions marked in intervals. * - * @param deletions + * @param shifts * @param intervals * @return */ - private boolean pruneIntervalVector(Vector deletions, Vector intervals) + private boolean pruneIntervalVector(List shifts, Vector intervals) { boolean pruned = false; - int i = 0, j = intervals.size() - 1, s = 0, t = deletions.size() - 1; + int i = 0, j = intervals.size() - 1, s = 0, t = shifts.size() - 1; int hr[] = (int[]) intervals.elementAt(i); - int sr[] = (int[]) deletions.elementAt(s); + int sr[] = shifts.get(s); while (i <= j && s <= t) { boolean trailinghn = hr[1] >= sr[0]; @@ -354,7 +354,7 @@ public class ColumnSelection { // leadinghc disjoint or not a deletion if (s < t) { - sr = (int[]) deletions.elementAt(++s); + sr = shifts.get(++s); } else { @@ -400,7 +400,7 @@ public class ColumnSelection // sr contained in hr if (s < t) { - sr = (int[]) deletions.elementAt(++s); + sr = shifts.get(++s); } else { @@ -414,10 +414,10 @@ public class ColumnSelection // operations. } - private boolean pruneColumnList(Vector deletion, Vector list) + private boolean pruneColumnList(List shifts, Vector list) { - int s = 0, t = deletion.size(); - int[] sr = (int[]) list.elementAt(s++); + int s = 0, t = shifts.size(); + int[] sr = shifts.get(s++); boolean pruned = false; int i = 0, j = list.size(); while (i < j && s <= t) @@ -434,7 +434,7 @@ public class ColumnSelection { if (s < t) { - sr = (int[]) deletion.elementAt(s); + sr = shifts.get(s); } s++; } @@ -453,7 +453,7 @@ public class ColumnSelection { if (deletions != null) { - Vector shifts = deletions.shifts; + List shifts = deletions.getShifts(); if (shifts != null && shifts.size() > 0) { // delete any intervals intersecting. @@ -485,8 +485,8 @@ public class ColumnSelection */ public List getHiddenColumns() { - return hiddenColumns == null ? Arrays.asList(new int[] - {}) : hiddenColumns; + return hiddenColumns == null ? Collections. emptyList() + : hiddenColumns; } /** diff --git a/src/jalview/datamodel/Mapping.java b/src/jalview/datamodel/Mapping.java index cb87719..f2c16d0 100644 --- a/src/jalview/datamodel/Mapping.java +++ b/src/jalview/datamodel/Mapping.java @@ -20,21 +20,21 @@ */ package jalview.datamodel; -import java.util.Vector; - import jalview.util.MapList; +import java.util.Vector; + public class Mapping { /** * Contains the start-end pairs mapping from the associated sequence to the - * sequence in the database coordinate system it also takes care of step - * difference between coordinate systems + * sequence in the database coordinate system. It also takes care of step + * difference between coordinate systems. */ MapList map = null; /** - * The seuqence that map maps the associated seuqence to (if any). + * The sequence that map maps the associated sequence to (if any). */ SequenceI to = null; @@ -111,19 +111,31 @@ public class Mapping * @param other * @return */ - public boolean equals(Mapping other) + @Override + public boolean equals(Object o) { - if (other == null) + if (o == null || !(o instanceof Mapping)) + { return false; + } + Mapping other = (Mapping) o; if (other == this) + { return true; + } if (other.to != to) + { return false; + } if ((map != null && other.map == null) || (map == null && other.map != null)) + { return false; + } if (map.equals(other.map)) + { return true; + } return false; } @@ -251,7 +263,9 @@ public class Mapping vf[v].setBegin(frange[i]); vf[v].setEnd(frange[i + 1]); if (frange.length > 2) + { vf[v].setDescription(f.getDescription() + "\nPart " + (v + 1)); + } } return vf; } @@ -300,14 +314,18 @@ public class Mapping from = (map.getToLowest() < from) ? from : map.getToLowest(); to = (map.getToHighest() > to) ? to : map.getToHighest(); if (from > to) + { return null; + } } else { from = (map.getToHighest() > from) ? from : map.getToHighest(); to = (map.getToLowest() < to) ? to : map.getToLowest(); if (from < to) + { return null; + } } return map.locateInFrom(from, to); } @@ -333,14 +351,18 @@ public class Mapping from = (map.getFromLowest() < from) ? from : map.getFromLowest(); to = (map.getFromHighest() > to) ? to : map.getFromHighest(); if (from > to) + { return null; + } } else { from = (map.getFromHighest() > from) ? from : map.getFromHighest(); to = (map.getFromLowest() < to) ? to : map.getFromLowest(); if (from < to) + { return null; + } } return map.locateInTo(from, to); } diff --git a/src/jalview/datamodel/SearchResults.java b/src/jalview/datamodel/SearchResults.java index 8434e81..7a241fd 100755 --- a/src/jalview/datamodel/SearchResults.java +++ b/src/jalview/datamodel/SearchResults.java @@ -20,10 +20,51 @@ */ package jalview.datamodel; +import java.util.ArrayList; +import java.util.List; + +/** + * Holds a list of search result matches, where each match is a contiguous + * stretch of a single sequence. + * + * @author gmcarstairs + * + */ public class SearchResults { - Match[] matches; + private List matches = new ArrayList(); + + public class Match + { + SequenceI sequence; + + int start; + + int end; + + public Match(SequenceI seq, int start, int end) + { + sequence = seq; + this.start = start; + this.end = end; + } + + public SequenceI getSequence() + { + return sequence; + } + + public int getStart() + { + return start; + } + + public int getEnd() + { + return end; + } + } /** * This method replaces the old search results which merely held an alignment @@ -39,25 +80,7 @@ public class SearchResults */ public void addResult(SequenceI seq, int start, int end) { - if (matches == null) - { - matches = new Match[] - { new Match(seq, start, end) }; - return; - } - - int mSize = matches.length; - - Match[] tmp = new Match[mSize + 1]; - int m; - for (m = 0; m < mSize; m++) - { - tmp[m] = matches[m]; - } - - tmp[m] = new Match(seq, start, end); - - matches = tmp; + matches.add(new Match(seq, start, end)); } /** @@ -69,15 +92,11 @@ public class SearchResults */ public boolean involvesSequence(SequenceI sequence) { - if (matches == null || matches.length == 0) - { - return false; - } SequenceI ds = sequence.getDatasetSequence(); - for (int m = 0; m < matches.length; m++) + for (Match m : matches) { - if (matches[m].sequence != null - && (matches[m].sequence == sequence || matches[m].sequence == ds)) + if (m.sequence != null + && (m.sequence == sequence || m.sequence == ds)) { return true; } @@ -92,7 +111,7 @@ public class SearchResults */ public int[] getResults(SequenceI sequence, int start, int end) { - if (matches == null) + if (matches.isEmpty()) { return null; } @@ -101,22 +120,22 @@ public class SearchResults int[] tmp = null; int resultLength, matchStart = 0, matchEnd = 0; boolean mfound; - for (int m = 0; m < matches.length; m++) + for (Match m : matches) { mfound = false; - if (matches[m].sequence == sequence) + if (m.sequence == sequence) { mfound = true; // locate aligned position - matchStart = sequence.findIndex(matches[m].start) - 1; - matchEnd = sequence.findIndex(matches[m].end) - 1; + matchStart = sequence.findIndex(m.start) - 1; + matchEnd = sequence.findIndex(m.end) - 1; } - else if (matches[m].sequence == sequence.getDatasetSequence()) + else if (m.sequence == sequence.getDatasetSequence()) { mfound = true; // locate region in local context - matchStart = sequence.findIndex(matches[m].start) - 1; - matchEnd = sequence.findIndex(matches[m].end) - 1; + matchStart = sequence.findIndex(m.start) - 1; + matchEnd = sequence.findIndex(m.end) - 1; } if (mfound) { @@ -160,37 +179,53 @@ public class SearchResults public int getSize() { - return matches == null ? 0 : matches.length; + return matches.size(); } public SequenceI getResultSequence(int index) { - return matches[index].sequence; + return matches.get(index).sequence; } - public int getResultStart(int index) + /** + * Returns the start position of the i'th match in the search results. + * + * @param i + * @return + */ + public int getResultStart(int i) { - return matches[index].start; + return matches.get(i).start; } - public int getResultEnd(int index) + /** + * Returns the end position of the i'th match in the search results. + * + * @param i + * @return + */ + public int getResultEnd(int i) { - return matches[index].end; + return matches.get(i).end; } - class Match + /** + * Returns true if no search result matches are held. + * + * @return + */ + public boolean isEmpty() { - SequenceI sequence; - - int start; - - int end; + return matches.isEmpty(); + } - public Match(SequenceI seq, int start, int end) - { - sequence = seq; - this.start = start; - this.end = end; - } + /** + * Returns the list of matches. + * + * @return + */ + public List getResults() + { + return matches; } } diff --git a/src/jalview/datamodel/Sequence.java b/src/jalview/datamodel/Sequence.java index 96e469a..963a7cf 100755 --- a/src/jalview/datamodel/Sequence.java +++ b/src/jalview/datamodel/Sequence.java @@ -725,6 +725,7 @@ public class Sequence implements SequenceI { return; } + // TODO use StringUtils.deleteChars char[] tmp; @@ -1024,7 +1025,7 @@ public class Sequence implements SequenceI 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); } } @@ -1245,8 +1246,10 @@ public class Sequence implements SequenceI String label) { List result = new ArrayList(); - 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)) { diff --git a/src/jalview/datamodel/SequenceGroup.java b/src/jalview/datamodel/SequenceGroup.java index 752c6d4..9c046c6 100755 --- a/src/jalview/datamodel/SequenceGroup.java +++ b/src/jalview/datamodel/SequenceGroup.java @@ -27,7 +27,6 @@ import jalview.schemes.ResidueProperties; import java.awt.Color; import java.util.ArrayList; -import java.util.Enumeration; import java.util.Hashtable; import java.util.List; import java.util.Map; @@ -160,13 +159,10 @@ public class SequenceGroup implements AnnotatedCollectionI { if (seqsel != null) { - sequences = new Vector(); - Enumeration sq = seqsel.sequences.elements(); - while (sq.hasMoreElements()) + for (SequenceI seq : seqsel.sequences) { - sequences.addElement(sq.nextElement()); + sequences.addElement(seq); } - ; if (seqsel.groupName != null) { groupName = new String(seqsel.groupName); @@ -989,12 +985,15 @@ public class SequenceGroup implements AnnotatedCollectionI { SequenceGroup sgroup = new SequenceGroup(this); SequenceI[] insect = getSequencesInOrder(alignment); - sgroup.sequences = new Vector(); - for (int s = 0; insect != null && s < insect.length; s++) + sgroup.sequences = new Vector(); + if (insect != null) { - if (map == null || map.containsKey(insect[s])) + for (SequenceI seq : insect) { - sgroup.sequences.addElement(insect[s]); + if (map == null || map.containsKey(seq)) + { + sgroup.sequences.addElement(seq); + } } } // Enumeration en =getSequences(hashtable).elements(); diff --git a/src/jalview/datamodel/SequenceI.java b/src/jalview/datamodel/SequenceI.java index e8c1d71..0c29d8c 100755 --- a/src/jalview/datamodel/SequenceI.java +++ b/src/jalview/datamodel/SequenceI.java @@ -172,8 +172,7 @@ public interface SequenceI public String getDescription(); /** - * Return the alignment column for a sequence position * Return the alignment - * position for a sequence position + * Return the alignment column for a sequence position * * @param pos * lying from start to end @@ -220,9 +219,9 @@ public interface SequenceI * 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); @@ -238,13 +237,12 @@ public interface SequenceI /** * 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! diff --git a/src/jalview/datamodel/xdb/embl/EmblEntry.java b/src/jalview/datamodel/xdb/embl/EmblEntry.java index 3c88083..47c732f 100644 --- a/src/jalview/datamodel/xdb/embl/EmblEntry.java +++ b/src/jalview/datamodel/xdb/embl/EmblEntry.java @@ -425,9 +425,13 @@ public class EmblEntry { 1, dna.getLength() }, 1, 1)); // TODO: transform EMBL Database refs to canonical form if (dbRefs != null) + { for (Iterator i = dbRefs.iterator(); i.hasNext(); dna .addDBRef((DBRefEntry) i.next())) + { ; + } + } } try { @@ -440,7 +444,9 @@ public class EmblEntry { for (Iterator dbr = feature.dbRefs.iterator(); dbr.hasNext(); dna .addDBRef((DBRefEntry) dbr.next())) + { ; + } } } if (FeatureProperties.isCodingFeature(sourceDb, feature.getName())) @@ -456,7 +462,9 @@ public class EmblEntry { for (Iterator dbr = feature.dbRefs.iterator(); dbr.hasNext(); dna .addDBRef((DBRefEntry) dbr.next())) + { ; + } } } } @@ -659,7 +667,9 @@ public class EmblEntry // { 1prstart, prstart + prseq.length() - 1 }, 3, 1); pcdnaref.setMap(new Mapping(mp)); if (product != null) + { product.addDBRef(pcdnaref); + } } } @@ -671,18 +681,20 @@ public class EmblEntry sf.setEnd(exon[xint + 1]); sf.setType(feature.getName()); sf.setFeatureGroup(sourceDb); - sf.setDescription("Exon " + (1 + (int) (xint / 2)) + sf.setDescription("Exon " + (1 + xint / 2) + " for protein '" + prname + "' EMBLCDS:" + prid); sf.setValue(FeatureProperties.EXONPOS, new Integer(1 + xint)); sf.setValue(FeatureProperties.EXONPRODUCT, prname); if (vals != null && vals.size() > 0) { - Enumeration kv = vals.elements(); + Enumeration kv = vals.keys(); while (kv.hasMoreElements()) { Object key = kv.nextElement(); if (key != null) + { sf.setValue(key.toString(), vals.get(key)); + } } } dna.addSequenceFeature(sf); diff --git a/src/jalview/ext/jmol/JalviewJmolBinding.java b/src/jalview/ext/jmol/JalviewJmolBinding.java index 619144e..9569bd0 100644 --- a/src/jalview/ext/jmol/JalviewJmolBinding.java +++ b/src/jalview/ext/jmol/JalviewJmolBinding.java @@ -43,7 +43,6 @@ import java.awt.event.ComponentListener; import java.io.File; import java.net.URL; import java.security.AccessControlException; -import java.util.Enumeration; import java.util.Hashtable; import java.util.Map; import java.util.Vector; @@ -1328,17 +1327,15 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel return; } - String res; int index; Color col; jmolHistory(false); // TODO: Switch between nucleotide or aa selection expressions - Enumeration en = ResidueProperties.aa3Hash.keys(); - StringBuffer command = new StringBuffer("select *;color white;"); - while (en.hasMoreElements()) + StringBuilder command = new StringBuilder(128); + command.append("select *;color white;"); + for (String res : ResidueProperties.aa3Hash.keySet()) { - res = en.nextElement().toString(); - index = ((Integer) ResidueProperties.aa3Hash.get(res)).intValue(); + index = ResidueProperties.aa3Hash.get(res).intValue(); if (index > 20) { continue; diff --git a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java index b345c5e..f511a65 100644 --- a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java +++ b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java @@ -1,23 +1,3 @@ -/* - * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2) - * Copyright (C) 2014 The Jalview Authors - * - * This file is part of Jalview. - * - * Jalview is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 - * of the License, or (at your option) any later version. - * - * Jalview is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Jalview. If not, see . - * The Jalview Authors are detailed in the 'AUTHORS' file. - */ package jalview.ext.rbvi.chimera; import jalview.api.AlignmentViewPanel; @@ -39,7 +19,6 @@ import jalview.util.MessageManager; import java.awt.Color; import java.util.ArrayList; -import java.util.Enumeration; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -693,7 +672,8 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel } AlignmentI alignment = alignmentv.getAlignment(); - for (jalview.structure.StructureMappingcommandSet cpdbbyseq : getColourBySequenceCommands(files, sr, fr, alignment)) + for (jalview.structure.StructureMappingcommandSet cpdbbyseq : getColourBySequenceCommands( + files, sr, fr, alignment)) { for (String command : cpdbbyseq.commands) { @@ -713,10 +693,8 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel String[] files, SequenceRenderer sr, FeatureRenderer fr, AlignmentI alignment) { - return ChimeraCommands - .getColourBySequenceCommand(getSsm(), files, getSequence(), sr, - fr, - alignment); + return ChimeraCommands.getColourBySequenceCommand(getSsm(), files, + getSequence(), sr, fr, alignment); } /** @@ -740,7 +718,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel } } - + // End StructureListener // ////////////////////////// @@ -877,7 +855,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel eval.append("." + chain); resetLastRes.append("." + chain); } - + viewer.sendChimeraCommand(eval.toString(), false); viewerCommandHistory(true); // viewer.startListening(); @@ -1140,20 +1118,17 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel return; } - String res; int index; Color col; // Chimera expects RBG values in the range 0-1 final double normalise = 255D; viewerCommandHistory(false); // TODO: Switch between nucleotide or aa selection expressions - Enumeration en = ResidueProperties.aa3Hash.keys(); StringBuilder command = new StringBuilder(128); command.append("color white;"); - while (en.hasMoreElements()) + for (String res : ResidueProperties.aa3Hash.keySet()) { - res = en.nextElement().toString(); - index = ((Integer) ResidueProperties.aa3Hash.get(res)).intValue(); + index = ResidueProperties.aa3Hash.get(res).intValue(); if (index > 20) { continue; @@ -1287,4 +1262,4 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel return chainNames; } -} +} \ No newline at end of file diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index 6e3024d..5ef3da3 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -23,13 +23,15 @@ package jalview.gui; import jalview.analysis.AAFrequency; import jalview.analysis.AlignmentSorter; import jalview.analysis.AlignmentUtils; +import jalview.analysis.AlignmentUtils.MappingResult; import jalview.analysis.Conservation; import jalview.analysis.CrossRef; -import jalview.analysis.NJTree; +import jalview.analysis.Dna; import jalview.analysis.ParseProperties; import jalview.analysis.SequenceIdMatcher; import jalview.api.AlignViewControllerGuiI; import jalview.api.AlignViewControllerI; +import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; import jalview.api.analysis.ScoreModelI; import jalview.bin.Cache; @@ -85,7 +87,9 @@ import jalview.schemes.TaylorColourScheme; import jalview.schemes.TurnColourScheme; import jalview.schemes.UserColourScheme; import jalview.schemes.ZappoColourScheme; +import jalview.structure.StructureSelectionManager; import jalview.util.MessageManager; +import jalview.viewmodel.AlignmentViewport; import jalview.ws.jws1.Discoverer; import jalview.ws.jws2.Jws2Discoverer; import jalview.ws.jws2.jabaws2.Jws2Instance; @@ -118,9 +122,11 @@ import java.io.File; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; +import java.util.Deque; import java.util.Enumeration; import java.util.Hashtable; import java.util.List; +import java.util.Set; import java.util.Vector; import javax.swing.JButton; @@ -148,19 +154,20 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, IProgressIndicator, AlignViewControllerGuiI { - /** DOCUMENT ME!! */ public static final int DEFAULT_WIDTH = 700; - /** DOCUMENT ME!! */ public static final int DEFAULT_HEIGHT = 500; + /* + * The currently displayed panel (selected tabbed view if more than one) + */ public AlignmentPanel alignPanel; AlignViewport viewport; public AlignViewControllerI avc; - Vector alignPanels = new Vector(); + List alignPanels = new ArrayList(); /** * Last format used to load or save alignments in this window @@ -369,6 +376,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, reload.setEnabled(true); } + /** + * Add a KeyListener with handlers for various KeyPressed and KeyReleased + * events + */ void addKeyListener() { addKeyListener(new KeyAdapter() @@ -597,7 +608,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, avc = new jalview.controller.AlignViewController(this, viewport, alignPanel); - alignPanels.addElement(ap); + alignPanels.add(ap); PaintRefresher.Register(ap, ap.av.getSequenceSetId()); @@ -640,7 +651,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, expandViews.setEnabled(true); gatherViews.setEnabled(true); tabbedPane.setVisible(true); - AlignmentPanel first = (AlignmentPanel) alignPanels.firstElement(); + AlignmentPanel first = alignPanels.get(0); tabbedPane.addTab(first.av.viewName, first); this.getContentPane().add(tabbedPane, BorderLayout.CENTER); } @@ -704,21 +715,102 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, public void setGUINucleotide(boolean nucleotide) { showTranslation.setVisible(nucleotide); + cdna.setVisible(!nucleotide); conservationMenuItem.setEnabled(!nucleotide); modifyConservation.setEnabled(!nucleotide); showGroupConservation.setEnabled(!nucleotide); rnahelicesColour.setEnabled(nucleotide); purinePyrimidineColour.setEnabled(nucleotide); - // Remember AlignFrame always starts as protein - // if (!nucleotide) - // { - // showTr - // calculateMenu.remove(calculateMenu.getItemCount() - 2); - // } } /** - * set up menus for the currently viewport. This may be called after any + * Builds codon mappings from this (protein) alignment to any compatible + * nucleotide alignments. Mappings are built between sequences with the same + * name and compatible lengths. Also makes the cDNA alignment a + * CommandListener for the protein alignment so that edits are mirrored. + */ + @Override + protected void linkCdna_actionPerformed() + { + int linkedCount = 0; + int alreadyLinkedCount = 0; + final AlignmentI thisAlignment = this.alignPanel.getAlignment(); + + for (AlignFrame af : Desktop.getAlignFrames()) + { + if (af.alignPanel != null) + { + final AlignmentI thatAlignment = af.alignPanel.getAlignment(); + if (thatAlignment.isNucleotide()) + { + MappingResult mapped = AlignmentUtils.mapProteinToCdna( + thisAlignment, thatAlignment); + if (mapped == MappingResult.AlreadyMapped) + { + alreadyLinkedCount++; + } + else if (mapped == MappingResult.Mapped) + { + final StructureSelectionManager ssm = StructureSelectionManager + .getStructureSelectionManager(Desktop.instance); + ssm.addMappings(thisAlignment.getCodonFrames()); + // enable the next line to enable linked editing + // ssm.addCommandListener(af.getViewport()); + linkedCount++; + } + } + } + } + String msg = ""; + if (linkedCount == 0 && alreadyLinkedCount == 0) + { + msg = MessageManager.getString("label.no_cdna"); + } + else if (linkedCount > 0) + { + msg = MessageManager.formatMessage("label.linked_cdna", linkedCount); + } + else + { + msg = MessageManager.formatMessage("label.cdna_all_linked", + alreadyLinkedCount); + } + setStatus(msg); + } + + /** + * Align any linked cDNA to match the alignment of this (protein) alignment. + * Any mapped sequence regions will be realigned, unmapped sequences are not + * affected. + */ + @Override + protected void alignCdna_actionPerformed() + { + int seqCount = 0; + int alignCount = 0; + final AlignmentI thisAlignment = this.alignPanel.getAlignment(); + for (AlignFrame af : Desktop.getAlignFrames()) + { + if (af.alignPanel != null) + { + final AlignmentI thatAlignment = af.alignPanel.getAlignment(); + if (thatAlignment.isNucleotide()) + { + int seqsAligned = thatAlignment.alignAs(thisAlignment); + seqCount += seqsAligned; + if (seqsAligned > 0) + { + af.alignPanel.alignmentChanged(); + alignCount++; + } + } + } + } + setStatus(MessageManager.formatMessage("label.cdna_aligned", seqCount, + alignCount)); + } + /** + * set up menus for the current viewport. This may be called after any * operation that affects the data in the current view (selection changed, * etc) to update the menus to reflect the new state. */ @@ -862,7 +954,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, public void actionPerformed(ActionEvent e) { handler.cancelActivity(id); - us.setProgressBar(MessageManager.formatMessage("label.cancelled_params", new String[]{((JLabel) progressPanel.getComponent(0)).getText()}), id); + us.setProgressBar(MessageManager.formatMessage("label.cancelled_params", new Object[]{((JLabel) progressPanel.getComponent(0)).getText()}), id); } }); progressPanel.add(cancel, BorderLayout.EAST); @@ -1033,7 +1125,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, if (value == JalviewFileChooser.APPROVE_OPTION) { currentFileFormat = chooser.getSelectedFormat(); - if (currentFileFormat == null) + while (currentFileFormat == null) { JOptionPane .showInternalMessageDialog( @@ -1043,8 +1135,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, MessageManager .getString("label.file_format_not_specified"), JOptionPane.WARNING_MESSAGE); + currentFileFormat = chooser.getSelectedFormat(); value = chooser.showSaveDialog(this); - return; + if (value != JalviewFileChooser.APPROVE_OPTION) + { + return; + } } fileName = chooser.getSelectedFile().getPath(); @@ -1141,7 +1237,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, this.setTitle(file); statusBar.setText(MessageManager.formatMessage( "label.successfully_saved_to_file_in_format", - new String[] + new Object[] { fileName, format })); } catch (Exception ex) { @@ -1154,7 +1250,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, if (!success) { JOptionPane.showInternalMessageDialog(this, MessageManager - .formatMessage("label.couldnt_save_file", new String[] + .formatMessage("label.couldnt_save_file", new Object[] { fileName }), MessageManager .getString("label.error_saving_file"), JOptionPane.WARNING_MESSAGE); @@ -1216,7 +1312,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, viewport.getAlignment(), omitHidden, viewport.getColumnSelection())); Desktop.addInternalFrame(cap, MessageManager.formatMessage( - "label.alignment_output_command", new String[] + "label.alignment_output_command", new Object[] { e.getActionCommand() }), 600, 500); } catch (OutOfMemoryError oom) { @@ -1362,7 +1458,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, // setClosed(true) is called for (int i = 0; i < alignPanels.size(); i++) { - AlignmentPanel ap = (AlignmentPanel) alignPanels.elementAt(i); + AlignmentPanel ap = alignPanels.get(i); ap.closePanel(); } } @@ -1392,7 +1488,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { int index = tabbedPane.getSelectedIndex(); int closedindex = tabbedPane.indexOfComponent(alignPanel2); - alignPanels.removeElement(alignPanel2); + alignPanels.remove(alignPanel2); // Unnecessary // if (viewport == alignPanel2.av) // { @@ -1419,12 +1515,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, 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[] + "label.undo_command", new Object[] { command.getDescription() })); } else @@ -1433,13 +1529,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, 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[] + "label.redo_command", new Object[] { command.getDescription() })); } else @@ -1453,8 +1549,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { if (command.getSize() > 0) { - viewport.historyList.push(command); - viewport.redoList.clear(); + viewport.addToHistoryList(command); + viewport.clearRedoList(); updateEditMenuBar(); viewport.updateHiddenColumns(); // viewport.hasHiddenColumns = (viewport.getColumnSelection() != null @@ -1472,11 +1568,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { if (alignPanels != null) { - Enumeration e = alignPanels.elements(); AlignmentI[] als = new AlignmentI[alignPanels.size()]; - for (int i = 0; e.hasMoreElements(); i++) + int i = 0; + for (AlignmentPanel ap : alignPanels) { - als[i] = ((AlignmentPanel) e.nextElement()).av.getAlignment(); + als[i++] = ap.av.getAlignment(); } return als; } @@ -1497,15 +1593,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, @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); command.undoCommand(getViewAlignments()); - AlignViewport originalSource = getOriginatingSource(command); + AlignmentViewport originalSource = getOriginatingSource(command); updateEditMenuBar(); if (originalSource != null) @@ -1535,16 +1631,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, @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); + AlignmentViewport originalSource = getOriginatingSource(command); updateEditMenuBar(); if (originalSource != null) @@ -1566,9 +1662,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } } - AlignViewport getOriginatingSource(CommandI command) + AlignmentViewport getOriginatingSource(CommandI command) { - AlignViewport originalSource = null; + AlignmentViewport originalSource = null; // For sequence removal and addition, we need to fire // the property change event FROM the viewport where the // original alignment was altered @@ -1577,16 +1673,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { EditCommand editCommand = (EditCommand) command; al = editCommand.getAlignment(); - Vector comps = (Vector) PaintRefresher.components.get(viewport + List comps = PaintRefresher.components.get(viewport .getSequenceSetId()); - for (int i = 0; i < comps.size(); i++) + for (Component comp : comps) { - if (comps.elementAt(i) instanceof AlignmentPanel) + if (comp instanceof AlignmentPanel) { - if (al == ((AlignmentPanel) comps.elementAt(i)).av.getAlignment()) + if (al == ((AlignmentPanel) comp).av.getAlignment()) { - originalSource = ((AlignmentPanel) comps.elementAt(i)).av; + originalSource = ((AlignmentPanel) comp).av; break; } } @@ -1625,11 +1721,23 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, viewport.getAlignment().moveSelectedSequencesByOne(sg, viewport.getHiddenRepSequences(), up); alignPanel.paintAlignment(true); + + final AlignViewportI peer = viewport.getCodingComplement(); + if (peer != null) + { + final SequenceGroup selectionGroup = peer.getSelectionGroup(); + if (selectionGroup != null) + { + peer.getAlignment().moveSelectedSequencesByOne( + peer.getSelectionGroup(), peer.getHiddenRepSequences(), up); + ((AlignViewport) peer).getAlignPanel().paintAlignment(true); + } + } } synchronized void slideSequences(boolean right, int size) { - List sg = new Vector(); + List sg = new ArrayList(); if (viewport.cursorMode) { sg.add(viewport.getAlignment().getSequenceAt( @@ -1648,13 +1756,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, return; } - Vector invertGroup = new Vector(); + List invertGroup = new ArrayList(); - for (int i = 0; i < viewport.getAlignment().getHeight(); i++) + for (SequenceI seq : viewport.getAlignment().getSequences()) { - if (!sg.contains(viewport.getAlignment().getSequenceAt(i))) + if (!sg.contains(seq)) { - invertGroup.add(viewport.getAlignment().getSequenceAt(i)); + invertGroup.add(seq); } } @@ -1663,7 +1771,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, SequenceI[] seqs2 = new SequenceI[invertGroup.size()]; for (int i = 0; i < invertGroup.size(); i++) { - seqs2[i] = (SequenceI) invertGroup.elementAt(i); + seqs2[i] = invertGroup.get(i); } SlideSequencesCommand ssc; @@ -1711,11 +1819,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } boolean appendHistoryItem = false; - if (viewport.historyList != null && viewport.historyList.size() > 0 - && viewport.historyList.peek() instanceof SlideSequencesCommand) + Deque historyList = viewport.getHistoryList(); + if (historyList != null + && historyList.size() > 0 + && historyList.peek() instanceof SlideSequencesCommand) { appendHistoryItem = ssc - .appendSlideCommand((SlideSequencesCommand) viewport.historyList + .appendSlideCommand((SlideSequencesCommand) historyList .peek()); } @@ -1791,7 +1901,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, Desktop.jalviewClipboard = new Object[] { seqs, viewport.getAlignment().getDataset(), hiddenColumns }; statusBar.setText(MessageManager.formatMessage( - "label.copied_sequences_to_clipboard", new String[] + "label.copied_sequences_to_clipboard", new Object[] { Integer.valueOf(seqs.length).toString() })); } @@ -2089,7 +2199,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, alignment.getSequences()); if (alignPanels != null) { - for (AlignmentPanel ap : ((Vector) alignPanels)) + for (AlignmentPanel ap : alignPanels) { ap.validateAnnotationDimensions(false); } @@ -2511,7 +2621,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, addHistoryItem(removeGapCols); statusBar.setText(MessageManager.formatMessage( - "label.removed_empty_columns", new String[] + "label.removed_empty_columns", new Object[] { Integer.valueOf(removeGapCols.getSize()).toString() })); // This is to maintain viewport position on first residue @@ -2582,16 +2692,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, .getSequences()); } - // else - { - // if (justifySeqs>0) - { - // alignment.justify(justifySeqs!=RIGHT_JUSTIFY); - } - } - - // } - /** * DOCUMENT ME! * @@ -2604,74 +2704,105 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, new Finder(); } + /** + * Create a new view of the current alignment. + */ @Override public void newView_actionPerformed(ActionEvent e) { - newView(true); - } + /* + * Note if the current view has a protein/cdna complementary view + */ + AlignViewportI linkedView = this.viewport.getCodingComplement(); - /** - * - * @param copyAnnotation - * if true then duplicate all annnotation, groups and settings - * @return new alignment panel, already displayed. - */ - public AlignmentPanel newView(boolean copyAnnotation) - { - return newView(null, copyAnnotation); - } + AlignmentPanel newPanel = newView(null, true); - /** - * - * @param viewTitle - * title of newly created view - * @return new alignment panel, already displayed. - */ - public AlignmentPanel newView(String viewTitle) - { - return newView(viewTitle, true); + /* + * If the original view has a protein/cdna linked view, make and link a new + * view there also. + */ + // TODO refactor the hell out of this - move to a controller, lose the casts + // and direct member access, etc + if (linkedView != null) + { + AlignFrame linkedAlignFrame = ((AlignViewport) linkedView) + .getAlignPanel().alignFrame; + AlignmentPanel newLinkedPanel = linkedAlignFrame.newView(null, true); + newLinkedPanel.av.viewName = newPanel.av.viewName; + newPanel.av.setCodingComplement(newLinkedPanel.av); + final StructureSelectionManager ssm = StructureSelectionManager + .getStructureSelectionManager(Desktop.instance); + ssm.addCommandListener(newPanel.av); + ssm.addCommandListener(newLinkedPanel.av); + + } } /** + * Creates and shows a new view of the current alignment. * * @param viewTitle - * title of newly created view + * title of newly created view; if null, one will be generated * @param copyAnnotation * if true then duplicate all annnotation, groups and settings * @return new alignment panel, already displayed. */ public AlignmentPanel newView(String viewTitle, boolean copyAnnotation) { + /* + * Create a new AlignmentPanel (with its own, new Viewport) + */ AlignmentPanel newap = new Jalview2XML().copyAlignPanel(alignPanel, true); if (!copyAnnotation) { - // just remove all the current annotation except for the automatic stuff + /* + * remove all groups and annotation except for the automatic stuff + */ newap.av.getAlignment().deleteAllGroups(); - for (AlignmentAnnotation alan : newap.av.getAlignment() - .getAlignmentAnnotation()) - { - if (!alan.autoCalculated) - { - newap.av.getAlignment().deleteAnnotation(alan); - } - ; - } + newap.av.getAlignment().deleteAllAnnotations(false); } newap.av.gatherViewsHere = false; if (viewport.viewName == null) { - viewport.viewName = "Original"; + viewport.viewName = MessageManager + .getString("label.view_name_original"); } - newap.av.historyList = viewport.historyList; - newap.av.redoList = viewport.redoList; + /* + * Views share the same edits, undo and redo stacks, mappings. + */ + newap.av.setHistoryList(viewport.getHistoryList()); + newap.av.setRedoList(viewport.getRedoList()); + newap.av.getAlignment().setCodonFrames( + viewport.getAlignment().getCodonFrames()); + + newap.av.viewName = getNewViewName(viewTitle); + + addAlignmentPanel(newap, true); + newap.alignmentChanged(); + if (alignPanels.size() == 2) + { + viewport.gatherViewsHere = true; + } + tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1); + return newap; + } + + /** + * Make a new name for the view, ensuring it is unique within the current + * sequenceSetId. (This used to be essential for Jalview Project archives, but + * these now use viewId. Unique view names are still desirable for usability.) + * + * @param viewTitle + * @return + */ + protected String getNewViewName(String viewTitle) + { int index = Desktop.getViewCount(viewport.getSequenceSetId()); - // make sure the new view has a unique name - this is essential for Jalview - // 2 archives boolean addFirstIndex = false; if (viewTitle == null || viewTitle.trim().length() == 0) { @@ -2683,45 +2814,55 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, index = 1;// we count from 1 if given a specific name } String newViewName = viewTitle + ((addFirstIndex) ? " " + index : ""); - Vector comps = (Vector) PaintRefresher.components.get(viewport + + List comps = PaintRefresher.components.get(viewport .getSequenceSetId()); - Vector existingNames = new Vector(); - for (int i = 0; i < comps.size(); i++) - { - if (comps.elementAt(i) instanceof AlignmentPanel) - { - AlignmentPanel ap = (AlignmentPanel) comps.elementAt(i); - if (!existingNames.contains(ap.av.viewName)) - { - existingNames.addElement(ap.av.viewName); - } - } - } + + List existingNames = getExistingViewNames(comps); while (existingNames.contains(newViewName)) { newViewName = viewTitle + " " + (++index); } + return newViewName; + } - newap.av.viewName = newViewName; - - addAlignmentPanel(newap, true); - newap.alignmentChanged(); - - if (alignPanels.size() == 2) + /** + * Returns a list of distinct view names found in the given list of + * components. View names are held on the viewport of an AlignmentPanel. + * + * @param comps + * @return + */ + protected List getExistingViewNames(List comps) + { + List existingNames = new ArrayList(); + for (Component comp : comps) { - viewport.gatherViewsHere = true; + if (comp instanceof AlignmentPanel) + { + AlignmentPanel ap = (AlignmentPanel) comp; + if (!existingNames.contains(ap.av.viewName)) + { + existingNames.add(ap.av.viewName); + } + } } - tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1); - return newap; + return existingNames; } + /** + * Explode tabbed views into separate windows. + */ @Override public void expandViews_actionPerformed(ActionEvent e) { Desktop.instance.explodeViews(this); } + /** + * Gather views in separate windows back into a tabbed presentation. + */ @Override public void gatherViews_actionPerformed(ActionEvent e) { @@ -3130,13 +3271,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, StringBuffer contents = new AlignmentProperties(viewport.getAlignment()) .formatAsHtml(); editPane.setText(MessageManager.formatMessage("label.html_content", - new String[] + new Object[] { contents.toString() })); JInternalFrame frame = new JInternalFrame(); frame.getContentPane().add(new JScrollPane(editPane)); - Desktop.instance.addInternalFrame(frame, MessageManager.formatMessage( - "label.alignment_properties", new String[] + Desktop.addInternalFrame(frame, MessageManager.formatMessage( + "label.alignment_properties", new Object[] { getTitle() }), 500, 400); } @@ -3158,7 +3299,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, OverviewPanel overview = new OverviewPanel(alignPanel); frame.setContentPane(overview); Desktop.addInternalFrame(frame, MessageManager.formatMessage( - "label.overview_params", new String[] + "label.overview_params", new Object[] { this.getTitle() }), frame.getWidth(), frame.getHeight()); frame.pack(); frame.setLayer(JLayeredPane.PALETTE_LAYER); @@ -3565,8 +3706,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { Component[] menuItems = colourMenu.getMenuComponents(); - int i, iSize = menuItems.length; - for (i = 0; i < iSize; i++) + int iSize = menuItems.length; + for (int i = 0; i < iSize; i++) { if (menuItems[i].getName() != null && menuItems[i].getName().equals("USER_DEFINED")) @@ -3827,7 +3968,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, @Override public void averageDistanceTreeMenuItem_actionPerformed(ActionEvent e) { - NewTreePanel("AV", "PID", "Average distance tree using PID"); + newTreePanel("AV", "PID", "Average distance tree using PID"); } /** @@ -3839,7 +3980,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, @Override public void neighbourTreeMenuItem_actionPerformed(ActionEvent e) { - NewTreePanel("NJ", "PID", "Neighbour joining tree using PID"); + newTreePanel("NJ", "PID", "Neighbour joining tree using PID"); } /** @@ -3851,7 +3992,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, @Override protected void njTreeBlosumMenuItem_actionPerformed(ActionEvent e) { - NewTreePanel("NJ", "BL", "Neighbour joining tree using BLOSUM62"); + newTreePanel("NJ", "BL", "Neighbour joining tree using BLOSUM62"); } /** @@ -3863,7 +4004,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, @Override protected void avTreeBlosumMenuItem_actionPerformed(ActionEvent e) { - NewTreePanel("AV", "BL", "Average distance tree using BLOSUM62"); + newTreePanel("AV", "BL", "Average distance tree using BLOSUM62"); } /** @@ -3876,7 +4017,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param title * DOCUMENT ME! */ - void NewTreePanel(String type, String pwType, String title) + void newTreePanel(String type, String pwType, String title) { TreePanel tp; @@ -3967,7 +4108,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, public void addSortByOrderMenuItem(String title, final AlignmentOrder order) { - final JMenuItem item = new JMenuItem(MessageManager.formatMessage("action.by_title_param", new String[]{title})); + final JMenuItem item = new JMenuItem(MessageManager.formatMessage("action.by_title_param", new Object[]{title})); sort.add(item); item.addActionListener(new java.awt.event.ActionListener() { @@ -4091,7 +4232,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { String treecalcnm = MessageManager.getString("label.tree_calc_" + type.toLowerCase()); - for (final Object pwtype : ResidueProperties.scoreMatrices.keySet()) + for (final String pwtype : ResidueProperties.scoreMatrices.keySet()) { JMenuItem tm = new JMenuItem(); ScoreModelI sm = ResidueProperties.scoreMatrices.get(pwtype); @@ -4107,7 +4248,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, @Override public void actionPerformed(ActionEvent e) { - NewTreePanel(type, (String) pwtype, title); + newTreePanel(type, pwtype, title); } }); calculateTree.add(tm); @@ -4117,21 +4258,18 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } sortByTreeMenu.removeAll(); - Vector comps = (Vector) PaintRefresher.components.get(viewport + List comps = PaintRefresher.components.get(viewport .getSequenceSetId()); - Vector treePanels = new Vector(); - int i, iSize = comps.size(); - for (i = 0; i < iSize; i++) + List treePanels = new ArrayList(); + for (Component comp : comps) { - if (comps.elementAt(i) instanceof TreePanel) + if (comp instanceof TreePanel) { - treePanels.add(comps.elementAt(i)); + treePanels.add((TreePanel) comp); } } - iSize = treePanels.size(); - - if (iSize < 1) + if (treePanels.size() < 1) { sortByTreeMenu.setVisible(false); return; @@ -4139,17 +4277,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, sortByTreeMenu.setVisible(true); - for (i = 0; i < treePanels.size(); i++) + for (final TreePanel tp : treePanels) { - final TreePanel tp = (TreePanel) treePanels.elementAt(i); final JMenuItem item = new JMenuItem(tp.getTitle()); - final NJTree tree = ((TreePanel) treePanels.elementAt(i)).getTree(); item.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(ActionEvent e) { - tp.sortByTree_actionPerformed(null); + tp.sortByTree_actionPerformed(); addHistoryItem(tp.sortAlignmentIn(alignPanel)); } @@ -4389,7 +4525,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } catch (Exception e) { } - ; } final AlignFrame me = this; buildingMenu = true; @@ -4544,14 +4679,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, .debug("Exception during web service menu building process.", e); } - ; } }); } catch (Exception e) { } - ; - buildingMenu = false; } }).start(); @@ -4697,7 +4829,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, public void run() { final long sttime = System.currentTimeMillis(); - ths.setProgressBar(MessageManager.formatMessage("status.searching_for_sequences_from", new String[]{fsrc}), sttime); + ths.setProgressBar(MessageManager.formatMessage("status.searching_for_sequences_from", new Object[]{fsrc}), sttime); try { Alignment ds = ths.getViewport().getAlignment().getDataset(); // update @@ -4721,12 +4853,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, sprods[s].updatePDBIds(); } Alignment al = new Alignment(sprods); - AlignedCodonFrame[] cf = prods.getCodonFrames(); + Set cf = prods.getCodonFrames(); al.setDataset(ds); - for (int s = 0; cf != null && s < cf.length; s++) + for (AlignedCodonFrame acf : cf) { - al.addCodonFrame(cf[s]); - cf[s] = null; + al.addCodonFrame(acf); } AlignFrame naf = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT); @@ -4753,7 +4884,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, jalview.bin.Cache.log.error("Error when finding crossreferences", e); } - ths.setProgressBar(MessageManager.formatMessage("status.finished_searching_for_sequences_from", new String[]{fsrc}), + ths.setProgressBar(MessageManager.formatMessage("status.finished_searching_for_sequences_from", new Object[]{fsrc}), sttime); } @@ -4779,92 +4910,49 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } } - @Override - public void showProducts_actionPerformed(ActionEvent e) - { - // ///////////////////////////// - // Collect Data to be translated/transferred - - SequenceI[] selection = viewport.getSequenceSelection(); - AlignmentI al = null; - try - { - al = jalview.analysis.Dna.CdnaTranslate(selection, viewport - .getViewAsVisibleContigs(true), viewport.getGapCharacter(), - viewport.getAlignment().getDataset()); - } catch (Exception ex) - { - al = null; - jalview.bin.Cache.log.debug("Exception during translation.", ex); - } - if (al == null) - { - JOptionPane - .showMessageDialog( - Desktop.desktop, - MessageManager - .getString("label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation"), - MessageManager.getString("label.translation_failed"), - JOptionPane.WARNING_MESSAGE); - } - else - { - AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT); - Desktop.addInternalFrame(af, MessageManager.formatMessage( - "label.translation_of_params", new String[] - { this.getTitle() }), DEFAULT_WIDTH, DEFAULT_HEIGHT); - } - } - + /** + * Construct and display a new frame containing the translation of this + * frame's cDNA sequences to their aligned protein (amino acid) equivalents. + */ @Override public void showTranslation_actionPerformed(ActionEvent e) { - // ///////////////////////////// - // Collect Data to be translated/transferred - - SequenceI[] selection = viewport.getSequenceSelection(); - String[] seqstring = viewport.getViewAsString(true); AlignmentI al = null; try { - al = jalview.analysis.Dna.CdnaTranslate(selection, seqstring, - viewport.getViewAsVisibleContigs(true), viewport - .getGapCharacter(), viewport.getAlignment() - .getAlignmentAnnotation(), viewport.getAlignment() - .getWidth(), viewport.getAlignment().getDataset()); + Dna dna = new Dna(viewport, viewport.getViewAsVisibleContigs(true)); + + al = dna.translateCdna(); } catch (Exception ex) { - al = null; jalview.bin.Cache.log.error( "Exception during translation. Please report this !", ex); - JOptionPane - .showMessageDialog( - Desktop.desktop, - MessageManager - .getString("label.error_when_translating_sequences_submit_bug_report"), - MessageManager - .getString("label.implementation_error") - + MessageManager - .getString("translation_failed"), - JOptionPane.ERROR_MESSAGE); + final String msg = MessageManager + .getString("label.error_when_translating_sequences_submit_bug_report"); + final String title = MessageManager + .getString("label.implementation_error") + + MessageManager.getString("translation_failed"); + JOptionPane.showMessageDialog(Desktop.desktop, msg, title, + JOptionPane.ERROR_MESSAGE); return; } - if (al == null) + if (al == null || al.getHeight() == 0) { - JOptionPane - .showMessageDialog( - Desktop.desktop, - MessageManager - .getString("label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation"), - MessageManager.getString("label.translation_failed"), - JOptionPane.WARNING_MESSAGE); + final String msg = MessageManager + .getString("label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation"); + final String title = MessageManager + .getString("label.translation_failed"); + JOptionPane.showMessageDialog(Desktop.desktop, msg, title, + JOptionPane.WARNING_MESSAGE); } else { AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT); Desktop.addInternalFrame(af, MessageManager.formatMessage( - "label.translation_of_params", new String[] + "label.translation_of_params", new Object[] { this.getTitle() }), DEFAULT_WIDTH, DEFAULT_HEIGHT); + // enable next line for linked editing + // viewport.getStructureSelectionManager().addCommandListener(viewport); } } @@ -5060,7 +5148,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, MessageManager .formatMessage( "label.automatically_associate_pdb_files_with_sequences_same_name", - new String[] + new Object[] { Integer.valueOf( filesmatched .size()) @@ -5103,7 +5191,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, ""+MessageManager .formatMessage( "label.ignore_unmatched_dropped_files_info", - new String[] + new Object[] { Integer.valueOf( filesnotmatched .size()) @@ -5211,7 +5299,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { jalview.io.JPredFile predictions = new jalview.io.JPredFile( file, protocol); - new JnetAnnotationMaker().add_annotation(predictions, + new JnetAnnotationMaker(); + JnetAnnotationMaker.add_annotation(predictions, viewport.getAlignment(), 0, false); isAnnotation = true; } @@ -5283,31 +5372,38 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } } + /** + * Method invoked by the ChangeListener on the tabbed pane, in other words + * when a different tabbed pane is selected by the user or programmatically. + */ @Override public void tabSelectionChanged(int index) { if (index > -1) { - alignPanel = (AlignmentPanel) alignPanels.elementAt(index); + alignPanel = alignPanels.get(index); viewport = alignPanel.av; avc.setViewportAndAlignmentPanel(viewport, alignPanel); setMenusFromViewport(viewport); } } + /** + * On right mouse click on view tab, prompt for and set new view name. + */ @Override public void tabbedPane_mousePressed(MouseEvent e) { if (SwingUtilities.isRightMouseButton(e)) { - String reply = JOptionPane.showInternalInputDialog(this, - MessageManager.getString("label.enter_view_name"), - MessageManager.getString("label.enter_view_name"), + String msg = MessageManager.getString("label.enter_view_name"); + String reply = JOptionPane.showInternalInputDialog(this, msg, msg, JOptionPane.QUESTION_MESSAGE); if (reply != null) { viewport.viewName = reply; + // TODO warn if reply is in getExistingViewNames()? tabbedPane.setTitleAt(tabbedPane.getSelectedIndex(), reply); } } @@ -5368,7 +5464,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @param av */ - public boolean closeView(AlignViewport av) + public boolean closeView(AlignmentViewport av) { if (viewport == av) { @@ -5514,7 +5610,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } }); - fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from", new String[]{src.getDbName()}))); + fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from", new Object[]{src.getDbName()}))); dfetch.add(fetchr); comp++; } @@ -5525,7 +5621,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, // fetch all entry DbSourceProxy src = otherdb.get(0); fetchr = new JMenuItem(MessageManager.formatMessage( - "label.fetch_all_param", new String[] + "label.fetch_all_param", new Object[] { src.getDbSource() })); fetchr.addActionListener(new ActionListener() { @@ -5547,11 +5643,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } }); - fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from_all_sources", new String[]{Integer.valueOf(otherdb.size()).toString(), src.getDbSource(), src.getDbName()}))); + fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from_all_sources", new Object[]{Integer.valueOf(otherdb.size()).toString(), src.getDbSource(), src.getDbName()}))); dfetch.add(fetchr); comp++; // and then build the rest of the individual menus - ifetch = new JMenu(MessageManager.formatMessage("label.source_from_db_source", new String[]{src.getDbSource()})); + ifetch = new JMenu(MessageManager.formatMessage("label.source_from_db_source", new Object[]{src.getDbSource()})); icomp = 0; String imname = null; int i = 0; @@ -5564,7 +5660,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, 0, 10) + "..." : dbname; if (imname == null) { - imname = MessageManager.formatMessage("label.from_msname", new String[]{sname}); + imname = MessageManager.formatMessage("label.from_msname", new Object[]{sname}); } fetchr = new JMenuItem(msname); final DbSourceProxy[] dassrc = @@ -5591,7 +5687,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, }); fetchr.setToolTipText("" - + MessageManager.formatMessage("label.fetch_retrieve_from", new String[]{dbname})); + + MessageManager.formatMessage("label.fetch_retrieve_from", new Object[]{dbname})); ifetch.add(fetchr); ++i; if (++icomp >= mcomp || i == (otherdb.size())) @@ -5845,11 +5941,60 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, /** * - * @return alignment panels in this alignemnt frame + * @return alignment panels in this alignment frame */ - public List getAlignPanels() + public List getAlignPanels() { - return alignPanels == null ? Arrays.asList(alignPanel) : alignPanels; + return alignPanels == null ? Arrays.asList(alignPanel) + : alignPanels; + } + + /** + * Open a new alignment window, with the cDNA associated with this (protein) + * alignment, aligned as is the protein. + */ + @Override + protected void viewAsCdna_actionPerformed() + { + final AlignmentI alignment = getViewport().getAlignment(); + Set mappings = alignment.getCodonFrames(); + if (mappings == null) + { + return; + } + List cdnaSeqs = new ArrayList(); + for (SequenceI aaSeq : alignment.getSequences()) { + for (AlignedCodonFrame acf : mappings) { + SequenceI dnaSeq = acf.getDnaForAaSeq(aaSeq.getDatasetSequence()); + if (dnaSeq != null) + { + /* + * There is a cDNA mapping for this protein sequence - add to new + * alignment. It will share the same dataset sequence as other mapped + * cDNA (no new mappings need to be created). + */ + final Sequence newSeq = new Sequence(dnaSeq); + newSeq.setDatasetSequence(dnaSeq); + cdnaSeqs.add(newSeq); + } + } + } + if (cdnaSeqs.size() == 0) + { + // show a warning dialog no mapped cDNA + return; + } + AlignmentI cdna = new Alignment(cdnaSeqs.toArray(new SequenceI[cdnaSeqs + .size()])); + AlignFrame alignFrame = new AlignFrame(cdna, AlignFrame.DEFAULT_WIDTH, + AlignFrame.DEFAULT_HEIGHT); + cdna.alignAs(alignment); + String newtitle = "cDNA " + MessageManager.getString("label.for") + " " + + this.title; + Desktop.addInternalFrame(alignFrame, newtitle, + AlignFrame.DEFAULT_WIDTH, + AlignFrame.DEFAULT_HEIGHT); + } } diff --git a/src/jalview/gui/AlignViewport.java b/src/jalview/gui/AlignViewport.java index 1c105d0..5698e0f 100644 --- a/src/jalview/gui/AlignViewport.java +++ b/src/jalview/gui/AlignViewport.java @@ -38,11 +38,14 @@ */ package jalview.gui; +import jalview.analysis.AlignmentUtils; +import jalview.analysis.AlignmentUtils.MappingResult; import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder; import jalview.analysis.NJTree; import jalview.api.AlignViewportI; import jalview.bin.Cache; import jalview.commands.CommandI; +import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentI; import jalview.datamodel.ColumnSelection; import jalview.datamodel.PDBEntry; @@ -51,9 +54,12 @@ import jalview.datamodel.SequenceGroup; 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 jalview.util.MessageManager; +import jalview.util.StringUtils; import jalview.viewmodel.AlignmentViewport; import jalview.ws.params.AutoCalcSetting; @@ -61,11 +67,17 @@ import java.awt.Color; import java.awt.Container; import java.awt.Font; import java.awt.Rectangle; +import java.io.File; +import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Deque; import java.util.Hashtable; -import java.util.Stack; +import java.util.Set; import java.util.Vector; +import javax.swing.JInternalFrame; +import javax.swing.JOptionPane; + /** * DOCUMENT ME! * @@ -73,7 +85,7 @@ import java.util.Vector; * @version $Revision: 1.141 $ */ public class AlignViewport extends AlignmentViewport implements - SelectionSource, VamsasSource, AlignViewportI + SelectionSource, VamsasSource, AlignViewportI, CommandListener { int startRes; @@ -129,9 +141,9 @@ public class AlignViewport extends AlignmentViewport implements boolean gatherViewsHere = false; - Stack historyList = new Stack(); + private Deque historyList = new ArrayDeque(); - Stack redoList = new Stack(); + private Deque redoList = new ArrayDeque(); int thresholdTextColour = 0; @@ -1018,6 +1030,9 @@ public class AlignViewport extends AlignmentViewport implements return followSelection; } + /** + * Send the current selection to be broadcast to any selection listeners. + */ public void sendSelection() { jalview.structure.StructureSelectionManager @@ -1038,7 +1053,6 @@ public class AlignViewport extends AlignmentViewport implements { AlignmentPanel[] aps = PaintRefresher.getAssociatedPanels(this .getSequenceSetId()); - AlignmentPanel ap = null; for (int p = 0; aps != null && p < aps.length; p++) { if (aps[p].av == this) @@ -1199,6 +1213,305 @@ public class AlignViewport extends AlignmentViewport implements 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: + *
    + *
  • compute the equivalent edit on the mapped sequences
  • + *
  • apply the mapped edit
  • + *
  • 'apply' the source edit to the working copy of the source sequences
  • + *
+ * + * @param command + * @param undo + * @param ssm + */ + @Override + public void mirrorCommand(CommandI command, boolean undo, + StructureSelectionManager ssm, VamsasSource source) + { + /* + * ...work in progress... do nothing unless we are a 'complement' of the + * source May replace this with direct calls not via SSM. + */ + if (source instanceof AlignViewportI + && ((AlignViewportI) source).getCodingComplement() == this) + { + // ok to continue; + } + else + { + return; + } + + CommandI mappedCommand = ssm.mapCommand(command, undo, getAlignment(), + getGapCharacter()); + if (mappedCommand != null) + { + AlignmentI[] views = getAlignPanel().alignFrame.getViewAlignments(); + mappedCommand.doCommand(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 list) + { + this.historyList = list; + } + + public Deque getHistoryList() + { + return this.historyList; + } + + public void setRedoList(Deque list) + { + this.redoList = list; + } + + public Deque getRedoList() + { + return this.redoList; + } + + /** + * Add the sequences from the given alignment to this viewport. Optionally, + * may give the user the option to open a new frame, or split panel, with cDNA + * and protein linked. + * + * @param al + * @param title + */ + public void addAlignment(AlignmentI al, String title) + { + // TODO: promote to AlignViewportI? applet CutAndPasteTransfer is different + + // refactored from FileLoader / CutAndPasteTransfer / SequenceFetcher with + // this comment: + // TODO: create undo object for this JAL-1101 + + /* + * If one alignment is protein and one nucleotide, with at least one + * sequence name in common, offer to open a linked alignment. + */ + if (getAlignment().isNucleotide() != al.isNucleotide()) + { + final Set sequenceNames = getAlignment().getSequenceNames(); + sequenceNames.retainAll(al.getSequenceNames()); + if (!sequenceNames.isEmpty()) // at least one sequence name in both + { + if (openLinkedAlignment(al, title)) + { + return; + } + } + } + + for (int i = 0; i < al.getHeight(); i++) + { + getAlignment().addSequence(al.getSequenceAt(i)); + } + // TODO this call was done by SequenceFetcher but not FileLoader or + // CutAndPasteTransfer. Is it needed? + setEndSeq(getAlignment().getHeight()); + firePropertyChange("alignment", null, getAlignment().getSequences()); + } + + /** + * Show a dialog with the option to open and link (cDNA <-> protein) as a new + * alignment. Returns true if the new alignment was opened, false if not, + * because the user declined the offer. + * + * @param title + */ + protected boolean openLinkedAlignment(AlignmentI al, String title) + { + String[] options = new String[] + { MessageManager.getString("action.no"), + MessageManager.getString("label.split_window"), + MessageManager.getString("label.new_window"), }; + final String question = JvSwingUtils.wrapTooltip(true, + MessageManager.getString("label.open_linked_alignment?")); + int response = JOptionPane.showOptionDialog(Desktop.desktop, question, + MessageManager.getString("label.open_linked_alignment"), + JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, + options, options[0]); + + if (response != 1 && response != 2) + { + return false; + } + final boolean openSplitPane = (response == 1); + final boolean openInNewWindow = (response == 2); + + /* + * Create the AlignFrame first (which creates the new alignment's datasets), + * before attempting sequence mapping. + */ + AlignFrame newAlignFrame = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH, + AlignFrame.DEFAULT_HEIGHT); + newAlignFrame.setTitle(title); + + /* + * Identify protein and dna alignments. Make a copy of this one if opening + * in a new split pane. + */ + AlignmentI thisAlignment = openSplitPane ? new Alignment(getAlignment()) + : getAlignment(); + AlignmentI protein = al.isNucleotide() ? thisAlignment : al; + final AlignmentI cdna = al.isNucleotide() ? al : thisAlignment; + + newAlignFrame.statusBar.setText(MessageManager.formatMessage( + "label.successfully_loaded_file", new Object[] + { title })); + + // TODO if we want this (e.g. to enable reload of the alignment from file), + // we will need to add parameters to the stack. + // if (!protocol.equals(AppletFormatAdapter.PASTE)) + // { + // alignFrame.setFileName(file, format); + // } + + if (openInNewWindow) + { + Desktop.addInternalFrame(newAlignFrame, title, + AlignFrame.DEFAULT_WIDTH, + AlignFrame.DEFAULT_HEIGHT); + } + + /* + * Try to find mappings for at least one sequence. Any mappings made will be + * added to the protein alignment. + */ + MappingResult mapped = AlignmentUtils.mapProteinToCdna(protein, cdna); + final StructureSelectionManager ssm = StructureSelectionManager + .getStructureSelectionManager(Desktop.instance); + if (mapped != MappingResult.Mapped) + { + /* + * No mapping possible - warn the user, but leave window open. + */ + final String msg = JvSwingUtils.wrapTooltip(true, + MessageManager.getString("label.mapping_failed")); + JOptionPane.showInternalMessageDialog(Desktop.desktop, msg, + MessageManager.getString("label.no_mappings"), + JOptionPane.WARNING_MESSAGE); + } + + try + { + newAlignFrame.setMaximum(jalview.bin.Cache.getDefault( + "SHOW_FULLSCREEN", + false)); + } catch (java.beans.PropertyVetoException ex) + { + } + + if (openSplitPane) + { + /* + * Open in split pane. DNA sequence above, protein below. + */ + AlignFrame copyMe = new AlignFrame(thisAlignment, + AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); + copyMe.setTitle(getAlignPanel().alignFrame.getTitle()); + final AlignFrame proteinFrame = al.isNucleotide() ? copyMe + : newAlignFrame; + final AlignFrame cdnaFrame = al.isNucleotide() ? newAlignFrame + : copyMe; + protein = proteinFrame.viewport.getAlignment(); + + cdnaFrame.setVisible(true); + proteinFrame.setVisible(true); + String sep = String.valueOf(File.separatorChar); + String proteinShortName = StringUtils.getLastToken( + proteinFrame.getTitle(), sep); + String dnaShortName = StringUtils.getLastToken(cdnaFrame.getTitle(), + sep); + String linkedTitle = MessageManager.formatMessage( + "label.linked_view_title", dnaShortName, proteinShortName); + JInternalFrame splitFrame = new SplitFrame(cdnaFrame, proteinFrame); + Desktop.addInternalFrame(splitFrame, linkedTitle, + AlignFrame.DEFAULT_WIDTH, + AlignFrame.DEFAULT_HEIGHT); + + /* + * Set the frames to listen for each other's edit and sort commands. + */ + ssm.addCommandListener(cdnaFrame.getViewport()); + ssm.addCommandListener(proteinFrame.getViewport()); + + /* + * 'Coding complement' (dna/protein) views will mirror each others' edits, + * selections, sorting etc as decided from time to time by the relevant + * authorities. + */ + proteinFrame.getViewport().setCodingComplement(cdnaFrame.getViewport()); + } + + /* + * Register the mappings (held on the protein alignment) with the + * StructureSelectionManager (for mouseover linking). + */ + ssm.addMappings(protein.getCodonFrames()); + + return true; + } + public AnnotationColumnChooser getAnnotationColumnSelectionState() { return annotationColumnSelectionState; diff --git a/src/jalview/gui/AlignmentPanel.java b/src/jalview/gui/AlignmentPanel.java index 6517e70..949376d 100644 --- a/src/jalview/gui/AlignmentPanel.java +++ b/src/jalview/gui/AlignmentPanel.java @@ -94,9 +94,7 @@ public class AlignmentPanel extends GAlignmentPanel implements * Creates a new AlignmentPanel object. * * @param af - * DOCUMENT ME! * @param av - * DOCUMENT ME! */ public AlignmentPanel(AlignFrame af, final AlignViewport av) { @@ -652,7 +650,6 @@ public class AlignmentPanel extends GAlignmentPanel implements */ public void adjustmentValueChanged(AdjustmentEvent evt) { - int oldX = av.getStartRes(); int oldY = av.getStartSeq(); @@ -1149,8 +1146,7 @@ public class AlignmentPanel extends GAlignmentPanel implements if (alignFrame != null && !headless) { alignFrame.setProgressBar(MessageManager.formatMessage( - "status.saving_file", - new String[] + "status.saving_file", new Object[] { type.getLabel() }), progress); } try @@ -1474,6 +1470,9 @@ public class AlignmentPanel extends GAlignmentPanel implements .getStructureSelectionManager(); ssm.removeStructureViewerListener(getSeqPanel(), null); ssm.removeSelectionListener(getSeqPanel()); + ssm.removeEditListener(av); + ssm.removeStructureViewerListener(getSeqPanel(), null); + ssm.removeSelectionListener(getSeqPanel()); av.setAlignment(null); av = null; } diff --git a/src/jalview/gui/AnnotationLabels.java b/src/jalview/gui/AnnotationLabels.java index f732a36..6521503 100755 --- a/src/jalview/gui/AnnotationLabels.java +++ b/src/jalview/gui/AnnotationLabels.java @@ -683,7 +683,7 @@ public class AnnotationLabels extends JPanel implements MouseListener, pop.addSeparator(); // av and sequencegroup need to implement same interface for final JCheckBoxMenuItem cbmi = new JCheckBoxMenuItem( - MessageManager.getString("label.ignore_gaps_consensus"), + MessageManager.getString("label.ignore_gaps_consensus"), (aa[selectedRow].groupRef != null) ? aa[selectedRow].groupRef .getIgnoreGapsConsensus() : ap.av .getIgnoreGapsConsensus()); @@ -709,7 +709,7 @@ public class AnnotationLabels extends JPanel implements MouseListener, if (aaa.groupRef != null) { final JCheckBoxMenuItem chist = new JCheckBoxMenuItem( - MessageManager.getString("label.show_group_histogram"), + MessageManager.getString("label.show_group_histogram"), aa[selectedRow].groupRef.isShowConsensusHistogram()); chist.addActionListener(new ActionListener() { @@ -728,7 +728,7 @@ public class AnnotationLabels extends JPanel implements MouseListener, }); pop.add(chist); final JCheckBoxMenuItem cprofl = new JCheckBoxMenuItem( - MessageManager.getString("label.show_group_logo"), + MessageManager.getString("label.show_group_logo"), aa[selectedRow].groupRef.isShowSequenceLogo()); cprofl.addActionListener(new ActionListener() { @@ -747,7 +747,7 @@ public class AnnotationLabels extends JPanel implements MouseListener, }); pop.add(cprofl); final JCheckBoxMenuItem cproflnorm = new JCheckBoxMenuItem( - MessageManager.getString("label.normalise_group_logo"), + MessageManager.getString("label.normalise_group_logo"), aa[selectedRow].groupRef.isNormaliseSequenceLogo()); cproflnorm.addActionListener(new ActionListener() { @@ -772,7 +772,7 @@ public class AnnotationLabels extends JPanel implements MouseListener, else { final JCheckBoxMenuItem chist = new JCheckBoxMenuItem( - MessageManager.getString("label.show_histogram"), av.isShowConsensusHistogram()); + MessageManager.getString("label.show_histogram"), av.isShowConsensusHistogram()); chist.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) @@ -791,7 +791,7 @@ public class AnnotationLabels extends JPanel implements MouseListener, }); pop.add(chist); final JCheckBoxMenuItem cprof = new JCheckBoxMenuItem( - MessageManager.getString("label.show_logo"), av.isShowSequenceLogo()); + MessageManager.getString("label.show_logo"), av.isShowSequenceLogo()); cprof.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) @@ -810,7 +810,7 @@ public class AnnotationLabels extends JPanel implements MouseListener, }); pop.add(cprof); final JCheckBoxMenuItem cprofnorm = new JCheckBoxMenuItem( - MessageManager.getString("label.normalise_logo"), av.isNormaliseSequenceLogo()); + MessageManager.getString("label.normalise_logo"), av.isNormaliseSequenceLogo()); cprofnorm.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) diff --git a/src/jalview/gui/AppVarnaBinding.java b/src/jalview/gui/AppVarnaBinding.java index a738a2c..03799e1 100644 --- a/src/jalview/gui/AppVarnaBinding.java +++ b/src/jalview/gui/AppVarnaBinding.java @@ -20,6 +20,7 @@ */ package jalview.gui; +import jalview.structure.AtomSpec; import jalview.util.MessageManager; import java.awt.BorderLayout; @@ -827,8 +828,6 @@ public class AppVarnaBinding extends jalview.ext.varna.JalviewVarnaBinding public void onWarningEmitted(String s) { - // TODO Auto-generated method stub - } public void mouseClicked(MouseEvent e) @@ -852,127 +851,73 @@ public class AppVarnaBinding extends jalview.ext.varna.JalviewVarnaBinding public void mouseEntered(MouseEvent arg0) { - // TODO Auto-generated method stub - } public void mouseExited(MouseEvent arg0) { - // TODO Auto-generated method stub - } public void mousePressed(MouseEvent arg0) { - // TODO Auto-generated method stub - } public void mouseReleased(MouseEvent arg0) { - // TODO Auto-generated method stub - - } - - @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 return null; } @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 - } @Override public void updateColours(Object source) { - // TODO Auto-generated method stub - } @Override public void componentHidden(ComponentEvent e) { - // TODO Auto-generated method stub - } @Override public void componentMoved(ComponentEvent e) { - // TODO Auto-generated method stub - } @Override public void componentResized(ComponentEvent e) { - // TODO Auto-generated method stub - } @Override public void componentShown(ComponentEvent e) { - // TODO Auto-generated method stub - } @Override public void onStructureRedrawn() { - // TODO Auto-generated method stub - } @Override public void onZoomLevelChanged() { - // TODO Auto-generated method stub - } @Override public void onTranslationChanged() { - // TODO Auto-generated method stub + } + @Override + public void highlightAtoms(List atoms) + { } } - -/* - * public static void main(String[] args) { JTextField str = new - * JTextField("ATGC"); - * - * AppVarnaBinding vab = new AppVarnaBinding(); vab.varnagui.set_seq(str); - * vab.varnagui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - * vab.varnagui.pack(); vab.varnagui.setVisible(true); } } - */ diff --git a/src/jalview/gui/CutAndPasteHtmlTransfer.java b/src/jalview/gui/CutAndPasteHtmlTransfer.java index 7fd091f..40a02f8 100644 --- a/src/jalview/gui/CutAndPasteHtmlTransfer.java +++ b/src/jalview/gui/CutAndPasteHtmlTransfer.java @@ -33,6 +33,7 @@ import javax.swing.event.HyperlinkEvent.EventType; import jalview.io.*; import jalview.jbgui.*; import jalview.util.MessageManager; +import jalview.viewmodel.AlignmentViewport; /** * Cut'n'paste files into the desktop See JAL-1105 @@ -43,7 +44,7 @@ import jalview.util.MessageManager; public class CutAndPasteHtmlTransfer extends GCutAndPasteHtmlTransfer { - AlignViewport viewport; + AlignmentViewport viewport; public CutAndPasteHtmlTransfer() { @@ -102,7 +103,7 @@ public class CutAndPasteHtmlTransfer extends GCutAndPasteHtmlTransfer /** * DOCUMENT ME! */ - public void setForInput(AlignViewport viewport) + public void setForInput(AlignmentViewport viewport) { this.viewport = viewport; if (viewport != null) diff --git a/src/jalview/gui/CutAndPasteTransfer.java b/src/jalview/gui/CutAndPasteTransfer.java index 0c22b14..6705f42 100644 --- a/src/jalview/gui/CutAndPasteTransfer.java +++ b/src/jalview/gui/CutAndPasteTransfer.java @@ -20,16 +20,28 @@ */ package jalview.gui; -import java.awt.*; -import java.awt.datatransfer.*; -import java.awt.event.*; -import javax.swing.*; - -import jalview.datamodel.*; -import jalview.io.*; -import jalview.jbgui.*; +import jalview.datamodel.Alignment; +import jalview.io.FormatAdapter; +import jalview.io.IdentifyFile; +import jalview.io.JalviewFileChooser; +import jalview.io.JalviewFileView; +import jalview.jbgui.GCutAndPasteTransfer; import jalview.util.MessageManager; +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; + +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPopupMenu; +import javax.swing.SwingUtilities; + /** * Cut'n'paste files into the desktop See JAL-1105 * @@ -190,24 +202,19 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer if (al != null) { + String title = MessageManager.formatMessage( + "label.input_cut_paste_params", new String[] + { format }); if (viewport != null) { - for (int i = 0; i < al.getHeight(); i++) - { - viewport.getAlignment().addSequence(al.getSequenceAt(i)); - } - - viewport.firePropertyChange("alignment", null, viewport - .getAlignment().getSequences()); + viewport.addAlignment(al, title); } else { AlignFrame af = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); af.currentFileFormat = format; - Desktop.addInternalFrame(af, MessageManager.formatMessage( - "label.input_cut_paste_params", new String[] - { format }), AlignFrame.DEFAULT_WIDTH, + Desktop.addInternalFrame(af, title, AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); af.statusBar.setText(MessageManager .getString("label.successfully_pasted_alignment_file")); diff --git a/src/jalview/gui/Desktop.java b/src/jalview/gui/Desktop.java index 70c8355..4bb828c 100644 --- a/src/jalview/gui/Desktop.java +++ b/src/jalview/gui/Desktop.java @@ -30,6 +30,7 @@ import jalview.jbgui.GStructureViewer; import jalview.structure.StructureSelectionManager; import jalview.util.ImageMaker; import jalview.util.MessageManager; +import jalview.viewmodel.AlignmentViewport; import jalview.ws.params.ParamManager; import java.awt.BorderLayout; @@ -69,6 +70,7 @@ import java.lang.reflect.Constructor; import java.net.URL; import java.util.ArrayList; import java.util.Hashtable; +import java.util.List; import java.util.StringTokenizer; import java.util.Vector; import java.util.concurrent.ExecutorService; @@ -162,6 +164,8 @@ public class Desktop extends jalview.jbgui.GDesktop implements static final int yOffset = 30; + private static final int THREE = 3; + public static jalview.ws.jws1.Discoverer discoverer; public static Object[] jalviewClipboard; @@ -1087,7 +1091,10 @@ public class Desktop extends jalview.jbgui.GDesktop implements if (format.equals("URL NOT FOUND")) { JOptionPane.showInternalMessageDialog(Desktop.desktop, - MessageManager.formatMessage("label.couldnt_locate", new String[]{url}), MessageManager.getString("label.url_not_found"), + MessageManager.formatMessage("label.couldnt_locate", + new Object[] + { url }), MessageManager + .getString("label.url_not_found"), JOptionPane.WARNING_MESSAGE); return; @@ -1363,7 +1370,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements return; } - AlignViewport source = null, target = null; + AlignmentViewport source = null, target = null; if (frames[0] instanceof AlignFrame) { source = ((AlignFrame) frames[0]).getCurrentView(); @@ -1475,7 +1482,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements public void run() { - setProgressBar(MessageManager.formatMessage("label.saving_jalview_project", new String[]{choice.getName()}), + setProgressBar(MessageManager.formatMessage("label.saving_jalview_project", new Object[]{choice.getName()}), choice.hashCode()); jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice.getParent()); @@ -1495,7 +1502,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements ex); JOptionPane.showMessageDialog( me, - MessageManager.formatMessage("label.error_whilst_saving_current_state_to", new String[]{ choice.getName()}), + MessageManager.formatMessage("label.error_whilst_saving_current_state_to", new Object[]{ choice.getName()}), MessageManager.getString("label.couldnt_save_project"), JOptionPane.WARNING_MESSAGE); } @@ -1569,7 +1576,9 @@ public class Desktop extends jalview.jbgui.GDesktop implements { public void run() { - setProgressBar(MessageManager.formatMessage("label.loading_jalview_project", new String[]{choice}), + setProgressBar(MessageManager.formatMessage( + "label.loading_jalview_project", new Object[] + { choice }), choice.hashCode()); try { @@ -1582,7 +1591,11 @@ public class Desktop extends jalview.jbgui.GDesktop implements Cache.log.error("Problems whilst loading project from " + choice, ex); JOptionPane.showMessageDialog(Desktop.desktop, - MessageManager.formatMessage("label.error_whilst_loading_project_from", new String[]{choice}), + MessageManager + .formatMessage( + "label.error_whilst_loading_project_from", + new Object[] + { choice }), MessageManager.getString("label.couldnt_load_project"), JOptionPane.WARNING_MESSAGE); } setProgressBar(null, choice.hashCode()); @@ -1604,7 +1617,9 @@ public class Desktop extends jalview.jbgui.GDesktop implements { if (fileLoadingCount == 0) { - fileLoadingPanels.add(addProgressPanel(MessageManager.formatMessage("label.loading_file", new String[]{fileName}))); + fileLoadingPanels.add(addProgressPanel(MessageManager.formatMessage( + "label.loading_file", new Object[] + { fileName }))); } fileLoadingCount++; } @@ -1670,7 +1685,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements public static int getViewCount(String alignmentId) { - AlignViewport[] aps = getViewports(alignmentId); + AlignmentViewport[] aps = getViewports(alignmentId); return (aps == null) ? 0 : aps.length; } @@ -1681,39 +1696,41 @@ public class Desktop extends jalview.jbgui.GDesktop implements */ public static AlignmentPanel[] getAlignmentPanels(String alignmentId) { - int count = 0; if (Desktop.desktop == null) { // no frames created and in headless mode // TODO: verify that frames are recoverable when in headless mode return null; } - JInternalFrame[] frames = Desktop.desktop.getAllFrames(); - ArrayList aps = new ArrayList(); - for (int t = 0; t < frames.length; t++) + List aps = new ArrayList(); + AlignFrame[] frames = getAlignFrames(); + if (frames == null) { - if (frames[t] instanceof AlignFrame) + return null; + } + for (AlignFrame af : frames) + { + for (AlignmentPanel ap : af.alignPanels) { - AlignFrame af = (AlignFrame) frames[t]; - for (int a = 0; a < af.alignPanels.size(); a++) + if (alignmentId.equals(ap.av.getSequenceSetId())) { - if (alignmentId.equals(((AlignmentPanel) af.alignPanels - .elementAt(a)).av.getSequenceSetId())) - { - aps.add(af.alignPanels.elementAt(a)); - } + aps.add(ap); } } + // for (int a = 0; a < af.alignPanels.size(); a++) + // { + // if (alignmentId.equals(af.alignPanels + // .get(a).av.getSequenceSetId())) + // { + // aps.add(af.alignPanels.get(a)); + // } + // } } if (aps.size() == 0) { return null; } - AlignmentPanel[] vap = new AlignmentPanel[aps.size()]; - for (int t = 0; t < vap.length; t++) - { - vap[t] = (AlignmentPanel) aps.get(t); - } + AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]); return vap; } @@ -1724,44 +1741,36 @@ public class Desktop extends jalview.jbgui.GDesktop implements * unique alignment id * @return all viewports on the alignment bound to sequenceSetId */ - public static AlignViewport[] getViewports(String sequenceSetId) + public static AlignmentViewport[] getViewports(String sequenceSetId) { - Vector viewp = new Vector(); + List viewp = new ArrayList(); if (desktop != null) { - javax.swing.JInternalFrame[] frames = instance.getAllFrames(); + AlignFrame[] frames = Desktop.getAlignFrames(); - for (int t = 0; t < frames.length; t++) + for (AlignFrame afr : frames) { - if (frames[t] instanceof AlignFrame) + if (afr.getViewport().getSequenceSetId().equals(sequenceSetId)) { - AlignFrame afr = ((AlignFrame) frames[t]); - if (afr.getViewport().getSequenceSetId().equals(sequenceSetId)) + if (afr.alignPanels != null) { - if (afr.alignPanels != null) + for (AlignmentPanel ap : afr.alignPanels) { - for (int a = 0; a < afr.alignPanels.size(); a++) + if (sequenceSetId.equals(ap.av.getSequenceSetId())) { - if (sequenceSetId.equals(((AlignmentPanel) afr.alignPanels - .elementAt(a)).av.getSequenceSetId())) - { - viewp.addElement(((AlignmentPanel) afr.alignPanels - .elementAt(a)).av); - } + viewp.add(ap.av); } } - else - { - viewp.addElement(((AlignFrame) frames[t]).getViewport()); - } + } + else + { + viewp.add(afr.getViewport()); } } } if (viewp.size() > 0) { - AlignViewport[] vp = new AlignViewport[viewp.size()]; - viewp.copyInto(vp); - return vp; + return viewp.toArray(new AlignmentViewport[viewp.size()]); } } return null; @@ -1777,7 +1786,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements for (int i = 0; i < size; i++) { - AlignmentPanel ap = (AlignmentPanel) af.alignPanels.elementAt(i); + AlignmentPanel ap = af.alignPanels.get(i); AlignFrame newaf = new AlignFrame(ap); if (ap.av.explodedPosition != null && !ap.av.explodedPosition.equals(af.getBounds())) @@ -1811,7 +1820,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements boolean gatherThis = false; for (int a = 0; a < af.alignPanels.size(); a++) { - AlignmentPanel ap = (AlignmentPanel) af.alignPanels.elementAt(a); + AlignmentPanel ap = af.alignPanels.get(a); if (viewId.equals(ap.av.getSequenceSetId())) { gatherThis = true; @@ -1858,7 +1867,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements Desktop.desktop, MessageManager.formatMessage( "label.couldnt_import_as_vamsas_session", - new String[] + new Object[] { fle }), MessageManager .getString("label.vamsas_document_import_failed"), @@ -1935,14 +1944,14 @@ public class Desktop extends jalview.jbgui.GDesktop implements return false; } - setProgressBar(MessageManager.formatMessage("status.importing_vamsas_session_from", new String[]{file.getName()}), + setProgressBar(MessageManager.formatMessage("status.importing_vamsas_session_from", new Object[]{file.getName()}), file.hashCode()); try { v_client = new jalview.gui.VamsasApplication(this, file, null); } catch (Exception ex) { - setProgressBar(MessageManager.formatMessage("status.importing_vamsas_session_from", new String[]{file.getName()}), + setProgressBar(MessageManager.formatMessage("status.importing_vamsas_session_from", new Object[]{file.getName()}), file.hashCode()); jalview.bin.Cache.log.error( "New vamsas session from existing session file failed:", ex); @@ -1950,7 +1959,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements } setupVamsasConnectedGui(); v_client.initial_update(); // TODO: thread ? - setProgressBar(MessageManager.formatMessage("status.importing_vamsas_session_from", new String[]{file.getName()}), + setProgressBar(MessageManager.formatMessage("status.importing_vamsas_session_from", new Object[]{file.getName()}), file.hashCode()); return v_client.inSession(); } @@ -2053,7 +2062,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements JMenuItem sessit = new JMenuItem(); sessit.setText(sess[i]); sessit.setToolTipText(MessageManager.formatMessage( - "label.connect_to_session", new String[] + "label.connect_to_session", new Object[] { sess[i] })); final Desktop dsktp = this; final String mysesid = sess[i]; @@ -2117,7 +2126,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements if (value == JalviewFileChooser.APPROVE_OPTION) { java.io.File choice = chooser.getSelectedFile(); - JPanel progpanel = addProgressPanel(MessageManager.formatMessage("label.saving_vamsas_doc", new String[]{choice.getName()})); + JPanel progpanel = addProgressPanel(MessageManager.formatMessage("label.saving_vamsas_doc", new Object[]{choice.getName()})); jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice.getParent()); String warnmsg = null; String warnttl = null; @@ -2204,6 +2213,8 @@ public class Desktop extends jalview.jbgui.GDesktop implements public class MyDesktopPane extends JDesktopPane implements Runnable { + private static final float ONE_MB = 1048576f; + boolean showMemoryUsage = false; Runtime runtime; @@ -2243,9 +2254,9 @@ public class Desktop extends jalview.jbgui.GDesktop implements { try { - maxMemory = runtime.maxMemory() / 1048576f; - allocatedMemory = runtime.totalMemory() / 1048576f; - freeMemory = runtime.freeMemory() / 1048576f; + maxMemory = runtime.maxMemory() / ONE_MB; + allocatedMemory = runtime.totalMemory() / ONE_MB; + freeMemory = runtime.freeMemory() / ONE_MB; totalFreeMemory = freeMemory + (maxMemory - allocatedMemory); percentUsage = (totalFreeMemory / maxMemory) * 100; @@ -2279,7 +2290,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements { g.drawString(MessageManager.formatMessage( "label.memory_stats", - new String[] + new Object[] { df.format(totalFreeMemory), df.format(maxMemory), df.format(percentUsage) }), 10, getHeight() - fm.getHeight()); @@ -2319,8 +2330,10 @@ public class Desktop extends jalview.jbgui.GDesktop implements /** * Accessor method to quickly get all the AlignmentFrames loaded. + * + * @return an array of AlignFrame, or null if none found */ - public static AlignFrame[] getAlignframes() + public static AlignFrame[] getAlignFrames() { JInternalFrame[] frames = Desktop.desktop.getAllFrames(); @@ -2328,35 +2341,43 @@ public class Desktop extends jalview.jbgui.GDesktop implements { return null; } - Vector avp = new Vector(); - try + List avp = new ArrayList(); + // REVERSE ORDER + for (int i = frames.length - 1; i > -1; i--) { - // REVERSE ORDER - for (int i = frames.length - 1; i > -1; i--) + if (frames[i] instanceof AlignFrame) { - if (frames[i] instanceof AlignFrame) + avp.add((AlignFrame) frames[i]); + } + else if (frames[i] instanceof SplitFrame) + { + /* + * Also check for a split frame containing an AlignFrame + */ + SplitFrame sf = (SplitFrame) frames[i]; + if (sf.getTopComponent() instanceof AlignFrame) { - AlignFrame af = (AlignFrame) frames[i]; - avp.addElement(af); + avp.add((AlignFrame) sf.getTopComponent()); + } + if (sf.getBottomComponent() instanceof AlignFrame) + { + avp.add((AlignFrame) sf.getBottomComponent()); } } - } catch (Exception ex) - { - ex.printStackTrace(); } if (avp.size() == 0) { return null; } - AlignFrame afs[] = new AlignFrame[avp.size()]; - for (int i = 0, j = avp.size(); i < j; i++) - { - afs[i] = (AlignFrame) avp.elementAt(i); - } - avp.clear(); + AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]); return afs; } + /** + * Returns an array of any AppJmol frames in the Desktop (or null if none). + * + * @return + */ public GStructureViewer[] getJmols() { JInternalFrame[] frames = Desktop.desktop.getAllFrames(); @@ -2365,32 +2386,21 @@ public class Desktop extends jalview.jbgui.GDesktop implements { return null; } - Vector avp = new Vector(); - try + List avp = new ArrayList(); + // REVERSE ORDER + for (int i = frames.length - 1; i > -1; i--) { - // REVERSE ORDER - for (int i = frames.length - 1; i > -1; i--) + if (frames[i] instanceof AppJmol) { - if (frames[i] instanceof AppJmol) - { - GStructureViewer af = (GStructureViewer) frames[i]; - avp.addElement(af); - } + GStructureViewer af = (GStructureViewer) frames[i]; + avp.add(af); } - } catch (Exception ex) - { - ex.printStackTrace(); } if (avp.size() == 0) { return null; } - GStructureViewer afs[] = new GStructureViewer[avp.size()]; - for (int i = 0, j = avp.size(); i < j; i++) - { - afs[i] = (GStructureViewer) avp.elementAt(i); - } - avp.clear(); + GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]); return afs; } @@ -2491,7 +2501,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements public void actionPerformed(ActionEvent e) { handler.cancelActivity(id); - us.setProgressBar(MessageManager.formatMessage("label.cancelled_params", new String[]{((JLabel) progressPanel.getComponent(0)).getText()}), id); + us.setProgressBar(MessageManager.formatMessage("label.cancelled_params", new Object[]{((JLabel) progressPanel.getComponent(0)).getText()}), id); } }); progressPanel.add(cancel, BorderLayout.EAST); @@ -2514,12 +2524,12 @@ public class Desktop extends jalview.jbgui.GDesktop implements /** * This will return the first AlignFrame viewing AlignViewport av. It will - * break if there are more than one AlignFrames viewing a particular av. This + * break if there are more than one AlignFrames viewing a particular av. * * @param av * @return alignFrame for av */ - public static AlignFrame getAlignFrameFor(AlignViewport av) + public static AlignFrame getAlignFrameFor(AlignmentViewport av) { if (desktop != null) { @@ -2753,7 +2763,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements { if (progress != null) { - progress.setProgressBar(MessageManager.formatMessage("status.opening_params", new String[]{url}), this.hashCode()); + progress.setProgressBar(MessageManager.formatMessage("status.opening_params", new Object[]{url}), this.hashCode()); } jalview.util.BrowserLauncher.openURL(url); } catch (Exception ex) diff --git a/src/jalview/gui/FeatureSettings.java b/src/jalview/gui/FeatureSettings.java index 3475fe3..c23ad26 100644 --- a/src/jalview/gui/FeatureSettings.java +++ b/src/jalview/gui/FeatureSettings.java @@ -28,6 +28,7 @@ import jalview.io.JalviewFileChooser; import jalview.schemes.AnnotationColourGradient; import jalview.schemes.GraduatedColor; import jalview.util.MessageManager; +import jalview.viewmodel.AlignmentViewport; import jalview.ws.dbsources.das.api.jalviewSourceI; import java.awt.BorderLayout; @@ -1213,7 +1214,7 @@ public class FeatureSettings extends JPanel { SequenceI[] dataset, seqs; int iSize; - AlignViewport vp = af.getViewport(); + AlignmentViewport vp = af.getViewport(); if (vp.getSelectionGroup() != null && vp.getSelectionGroup().getSize() > 0) { diff --git a/src/jalview/gui/IdCanvas.java b/src/jalview/gui/IdCanvas.java index 3bc3168..21b1790 100755 --- a/src/jalview/gui/IdCanvas.java +++ b/src/jalview/gui/IdCanvas.java @@ -20,13 +20,19 @@ */ package jalview.gui; -import java.awt.*; -import java.awt.image.*; +import jalview.datamodel.SequenceI; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; import java.util.List; -import javax.swing.*; - -import jalview.datamodel.*; +import javax.swing.JPanel; /** * DOCUMENT ME! diff --git a/src/jalview/gui/IdPanel.java b/src/jalview/gui/IdPanel.java index a22e918..91592ad 100755 --- a/src/jalview/gui/IdPanel.java +++ b/src/jalview/gui/IdPanel.java @@ -363,6 +363,9 @@ public class IdPanel extends JPanel implements MouseListener, { selectSeq(seq); } + // TODO is this addition ok here? + av.isSelectionGroupChanged(true); + alignPanel.paintAlignment(true); } diff --git a/src/jalview/gui/Jalview2XML.java b/src/jalview/gui/Jalview2XML.java index a2cd147..917b6d9 100644 --- a/src/jalview/gui/Jalview2XML.java +++ b/src/jalview/gui/Jalview2XML.java @@ -22,6 +22,7 @@ package jalview.gui; import jalview.api.structures.JalviewStructureDisplayI; import jalview.bin.Cache; +import jalview.datamodel.AlignedCodonFrame; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; @@ -30,7 +31,6 @@ import jalview.datamodel.SequenceI; import jalview.datamodel.StructureViewerModel; import jalview.datamodel.StructureViewerModel.StructureData; import jalview.schemabinding.version2.AlcodMap; -import jalview.schemabinding.version2.Alcodon; import jalview.schemabinding.version2.AlcodonFrame; import jalview.schemabinding.version2.Annotation; import jalview.schemabinding.version2.AnnotationColours; @@ -359,7 +359,7 @@ public class Jalview2XML */ public void saveState(JarOutputStream jout) { - JInternalFrame[] frames = Desktop.desktop.getAllFrames(); + AlignFrame[] frames = Desktop.getAlignFrames(); // Desktop.desktop.getAllFrames(); if (frames == null) { @@ -379,67 +379,61 @@ public class Jalview2XML // REVERSE ORDER for (int i = frames.length - 1; i > -1; i--) { - if (frames[i] instanceof AlignFrame) + AlignFrame af = frames[i]; + // skip ? + if (skipList != null + && skipList + .containsKey(af.getViewport().getSequenceSetId())) { - AlignFrame af = (AlignFrame) frames[i]; - // skip ? - if (skipList != null - && skipList.containsKey(af.getViewport() - .getSequenceSetId())) - { - continue; - } + continue; + } + + String shortName = af.getTitle(); - String shortName = af.getTitle(); + if (shortName.indexOf(File.separatorChar) > -1) + { + shortName = shortName.substring(shortName + .lastIndexOf(File.separatorChar) + 1); + } + + int count = 1; - if (shortName.indexOf(File.separatorChar) > -1) + while (shortNames.contains(shortName)) + { + if (shortName.endsWith("_" + (count - 1))) { - shortName = shortName.substring(shortName - .lastIndexOf(File.separatorChar) + 1); + shortName = shortName.substring(0, shortName.lastIndexOf("_")); } - int count = 1; + shortName = shortName.concat("_" + count); + count++; + } - while (shortNames.contains(shortName)) - { - if (shortName.endsWith("_" + (count - 1))) - { - shortName = shortName - .substring(0, shortName.lastIndexOf("_")); - } + shortNames.addElement(shortName); - shortName = shortName.concat("_" + count); - count++; - } + if (!shortName.endsWith(".xml")) + { + shortName = shortName + ".xml"; + } - shortNames.addElement(shortName); + int ap, apSize = af.alignPanels.size(); - if (!shortName.endsWith(".xml")) + for (ap = 0; ap < apSize; ap++) + { + AlignmentPanel apanel = af.alignPanels.get(ap); + String fileName = apSize == 1 ? shortName : ap + shortName; + if (!fileName.endsWith(".xml")) { - shortName = shortName + ".xml"; + fileName = fileName + ".xml"; } - int ap, apSize = af.alignPanels.size(); + saveState(apanel, fileName, jout); - for (ap = 0; ap < apSize; ap++) + String dssid = getDatasetIdRef(af.getViewport().getAlignment() + .getDataset()); + if (!dsses.containsKey(dssid)) { - AlignmentPanel apanel = (AlignmentPanel) af.alignPanels - .elementAt(ap); - String fileName = apSize == 1 ? shortName : ap + shortName; - if (!fileName.endsWith(".xml")) - { - fileName = fileName + ".xml"; - } - - saveState(apanel, fileName, jout); - - String dssid = getDatasetIdRef(af.getViewport().getAlignment() - .getDataset()); - if (!dsses.containsKey(dssid)) - { - dsses.put(dssid, af); - } - + dsses.put(dssid, af); } } } @@ -473,15 +467,15 @@ public class Jalview2XML { try { - int ap, apSize = af.alignPanels.size(); + int ap = 0; + int apSize = af.alignPanels.size(); FileOutputStream fos = new FileOutputStream(jarFile); JarOutputStream jout = new JarOutputStream(fos); Hashtable dsses = new Hashtable(); - for (ap = 0; ap < apSize; ap++) + for (AlignmentPanel apanel : af.alignPanels) { - AlignmentPanel apanel = (AlignmentPanel) af.alignPanels - .elementAt(ap); String jfileName = apSize == 1 ? fileName : fileName + ap; + ap++; if (!jfileName.endsWith(".xml")) { jfileName = jfileName + ".xml"; @@ -780,8 +774,7 @@ public class Jalview2XML { byte[] data = new byte[(int) file.length()]; jout.putNextEntry(new JarEntry(entry.getId())); - dis = new DataInputStream( - new FileInputStream(file)); + dis = new DataInputStream(new FileInputStream(file)); dis.readFully(data); DataOutputStream dout = new DataOutputStream(jout); @@ -837,31 +830,18 @@ public class Jalview2XML jal = av.getAlignment(); } // SAVE MAPPINGS - if (jal.getCodonFrames() != null && jal.getCodonFrames().length > 0) + if (jal.getCodonFrames() != null) { - jalview.datamodel.AlignedCodonFrame[] jac = jal.getCodonFrames(); - for (int i = 0; i < jac.length; i++) + Set jac = jal.getCodonFrames(); + for (AlignedCodonFrame acf : jac) { AlcodonFrame alc = new AlcodonFrame(); vamsasSet.addAlcodonFrame(alc); - for (int p = 0; p < jac[i].aaWidth; p++) - { - Alcodon cmap = new Alcodon(); - if (jac[i].codons[p] != null) - { - // Null codons indicate a gapped column in the translated peptide - // alignment. - cmap.setPos1(jac[i].codons[p][0]); - cmap.setPos2(jac[i].codons[p][1]); - cmap.setPos3(jac[i].codons[p][2]); - } - alc.addAlcodon(cmap); - } - if (jac[i].getProtMappings() != null - && jac[i].getProtMappings().length > 0) + if (acf.getProtMappings() != null + && acf.getProtMappings().length > 0) { - SequenceI[] dnas = jac[i].getdnaSeqs(); - jalview.datamodel.Mapping[] pmaps = jac[i].getProtMappings(); + SequenceI[] dnas = acf.getdnaSeqs(); + jalview.datamodel.Mapping[] pmaps = acf.getProtMappings(); for (int m = 0; m < pmaps.length; m++) { AlcodMap alcmap = new AlcodMap(); @@ -871,6 +851,37 @@ public class Jalview2XML alc.addAlcodMap(alcmap); } } + +// { +// AlcodonFrame alc = new AlcodonFrame(); +// vamsasSet.addAlcodonFrame(alc); +// for (int p = 0; p < acf.aaWidth; p++) +// { +// Alcodon cmap = new Alcodon(); +// if (acf.codons[p] != null) +// { +// // Null codons indicate a gapped column in the translated peptide +// // alignment. +// cmap.setPos1(acf.codons[p][0]); +// cmap.setPos2(acf.codons[p][1]); +// cmap.setPos3(acf.codons[p][2]); +// } +// alc.addAlcodon(cmap); +// } +// if (acf.getProtMappings() != null +// && acf.getProtMappings().length > 0) +// { +// SequenceI[] dnas = acf.getdnaSeqs(); +// jalview.datamodel.Mapping[] pmaps = acf.getProtMappings(); +// for (int m = 0; m < pmaps.length; m++) +// { +// AlcodMap alcmap = new AlcodMap(); +// alcmap.setDnasq(seqHash(dnas[m])); +// alcmap.setMapping(createVamsasMapping(pmaps[m], dnas[m], null, +// false)); +// alc.addAlcodMap(alcmap); +// } +// } } } @@ -1124,8 +1135,9 @@ public class Jalview2XML { jalview.schemabinding.version2.FeatureSettings fs = new jalview.schemabinding.version2.FeatureSettings(); - String[] renderOrder = ap.getSeqPanel().seqCanvas.getFeatureRenderer() - .getRenderOrder().toArray(new String[0]); + String[] renderOrder = ap.getSeqPanel().seqCanvas + .getFeatureRenderer().getRenderOrder() + .toArray(new String[0]); Vector settingsAdded = new Vector(); Object gstyle = null; @@ -1152,7 +1164,8 @@ public class Jalview2XML } else { - setting.setColour(ap.getSeqPanel().seqCanvas.getFeatureRenderer() + setting.setColour(ap.getSeqPanel().seqCanvas + .getFeatureRenderer() .getColour(renderOrder[ro]).getRGB()); } @@ -1196,8 +1209,8 @@ public class Jalview2XML settingsAdded.addElement(key); } // is groups actually supposed to be a map here ? - en = ap.getSeqPanel().seqCanvas.getFeatureRenderer().getFeatureGroups() - .iterator(); + en = ap.getSeqPanel().seqCanvas.getFeatureRenderer() + .getFeatureGroups().iterator(); Vector groupsAdded = new Vector(); while (en.hasNext()) { @@ -1230,8 +1243,8 @@ public class Jalview2XML for (int c = 0; c < av.getColumnSelection().getHiddenColumns() .size(); c++) { - int[] region = av.getColumnSelection() - .getHiddenColumns().get(c); + int[] region = av.getColumnSelection().getHiddenColumns() + .get(c); HiddenColumns hc = new HiddenColumns(); hc.setStart(region[0]); hc.setEnd(region[1]); @@ -1302,17 +1315,14 @@ public class Jalview2XML Pdbids pdb, PDBEntry entry, List viewIds, String matchedFile, StructureViewerBase viewFrame) { - final AAStructureBindingModel bindingModel = viewFrame - .getBinding(); - for (int peid = 0; peid < bindingModel - .getPdbCount(); peid++) + final AAStructureBindingModel bindingModel = viewFrame.getBinding(); + for (int peid = 0; peid < bindingModel.getPdbCount(); peid++) { final PDBEntry pdbentry = bindingModel.getPdbEntry(peid); final String pdbId = pdbentry.getId(); if (!pdbId.equals(entry.getId()) && !(entry.getId().length() > 4 && entry.getId() - .toLowerCase() - .startsWith(pdbId.toLowerCase()))) + .toLowerCase().startsWith(pdbId.toLowerCase()))) { continue; } @@ -1320,8 +1330,7 @@ public class Jalview2XML { matchedFile = pdbentry.getFile(); } - else if (!matchedFile.equals(pdbentry - .getFile())) + else if (!matchedFile.equals(pdbentry.getFile())) { Cache.log .warn("Probably lost some PDB-Sequence mappings for this structure file (which apparently has same PDB Entry code): " @@ -1334,8 +1343,7 @@ public class Jalview2XML // 1QIP==1qipA) String statestring = viewFrame.getStateInfo(); - for (int smap = 0; smap < viewFrame.getBinding() - .getSequence()[peid].length; smap++) + for (int smap = 0; smap < viewFrame.getBinding().getSequence()[peid].length; smap++) { // if (jal.findIndex(jmol.jmb.sequence[peid][smap]) > -1) if (jds == viewFrame.getBinding().getSequence()[peid][smap]) @@ -1349,8 +1357,7 @@ public class Jalview2XML final String viewId = viewFrame.getViewId(); state.setViewId(viewId); state.setAlignwithAlignPanel(viewFrame.isUsedforaligment(ap)); - state.setColourwithAlignPanel(viewFrame - .isUsedforcolourby(ap)); + state.setColourwithAlignPanel(viewFrame.isUsedforcolourby(ap)); state.setColourByJmol(viewFrame.isColouredByViewer()); /* * Only store each structure viewer's state once in each XML document. @@ -1645,7 +1652,9 @@ public class Jalview2XML return false; } } - throw new Error(MessageManager.formatMessage("error.unsupported_version_calcIdparam", new String[]{calcIdParam.toString()})); + throw new Error(MessageManager.formatMessage( + "error.unsupported_version_calcIdparam", new String[] + { calcIdParam.toString() })); } /** @@ -2443,8 +2452,7 @@ public class Jalview2XML } } StructureSelectionManager.getStructureSelectionManager( - Desktop.instance) - .registerPDBEntry(entry); + Desktop.instance).registerPDBEntry(entry); al.getSequenceAt(i).getDatasetSequence().addPDBId(entry); } } @@ -2461,35 +2469,13 @@ public class Jalview2XML AlcodonFrame[] alc = vamsasSet.getAlcodonFrame(); for (int i = 0; i < alc.length; i++) { - jalview.datamodel.AlignedCodonFrame cf = new jalview.datamodel.AlignedCodonFrame( - alc[i].getAlcodonCount()); - if (alc[i].getAlcodonCount() > 0) - { - Alcodon[] alcods = alc[i].getAlcodon(); - for (int p = 0; p < cf.codons.length; p++) - { - if (alcods[p].hasPos1() && alcods[p].hasPos2() - && alcods[p].hasPos3()) - { - // translated codons require three valid positions - cf.codons[p] = new int[3]; - cf.codons[p][0] = (int) alcods[p].getPos1(); - cf.codons[p][1] = (int) alcods[p].getPos2(); - cf.codons[p][2] = (int) alcods[p].getPos3(); - } - else - { - cf.codons[p] = null; - } - } - } + AlignedCodonFrame cf = new AlignedCodonFrame(); if (alc[i].getAlcodMapCount() > 0) { AlcodMap[] maps = alc[i].getAlcodMap(); for (int m = 0; m < maps.length; m++) { - SequenceI dnaseq = seqRefIds - .get(maps[m].getDnasq()); + SequenceI dnaseq = seqRefIds.get(maps[m].getDnasq()); // Load Mapping jalview.datamodel.Mapping mapping = null; // attach to dna sequence reference. @@ -2511,7 +2497,6 @@ public class Jalview2XML } al.addCodonFrame(cf); } - } // //////////////////////////////// @@ -2766,8 +2751,7 @@ public class Jalview2XML for (int s = 0; s < groups[i].getSeqCount(); s++) { String seqId = groups[i].getSeq(s) + ""; - jalview.datamodel.SequenceI ts = seqRefIds - .get(seqId); + jalview.datamodel.SequenceI ts = seqRefIds.get(seqId); if (ts != null) { @@ -3057,10 +3041,10 @@ public class Jalview2XML for (int s = 0; s < structureStateCount; s++) { // check to see if we haven't already created this structure view - final StructureState structureState = ids[p].getStructureState(s); + final StructureState structureState = ids[p] + .getStructureState(s); String sviewid = (structureState.getViewId() == null) ? null - : structureState.getViewId() - + uniqueSetSuffix; + : structureState.getViewId() + uniqueSetSuffix; jalview.datamodel.PDBEntry jpdb = new jalview.datamodel.PDBEntry(); // Originally : ids[p].getFile() // : TODO: verify external PDB file recovery still works in normal @@ -3077,8 +3061,8 @@ public class Jalview2XML // Desktop.desktop.getComponentAt(x, y); // TODO: NOW: check that this recovers the PDB file correctly. String pdbFile = loadPDBFile(jprovider, ids[p].getId()); - jalview.datamodel.SequenceI seq = seqRefIds - .get(jseqs[i].getId() + ""); + jalview.datamodel.SequenceI seq = seqRefIds.get(jseqs[i] + .getId() + ""); if (sviewid == null) { sviewid = "_jalview_pre2_4_" + x + "," + y + "," + width @@ -3086,8 +3070,8 @@ public class Jalview2XML } if (!structureViewers.containsKey(sviewid)) { - structureViewers.put(sviewid, new StructureViewerModel(x, y, width, height, - false, false, true)); + structureViewers.put(sviewid, new StructureViewerModel(x, y, + width, height, false, false, true)); // Legacy pre-2.7 conversion JAL-823 : // do not assume any view has to be linked for colour by // sequence @@ -3117,8 +3101,7 @@ public class Jalview2XML * pre-2.7 projects) */ boolean colourByViewer = jmoldat.isColourByViewer(); - colourByViewer &= structureState - .hasColourByJmol() ? structureState + colourByViewer &= structureState.hasColourByJmol() ? structureState .getColourByJmol() : true; jmoldat.setColourByViewer(colourByViewer); @@ -3155,11 +3138,12 @@ public class Jalview2XML } } } - // Instantiate the associated structure views - for (Entry entry : structureViewers.entrySet()) + // Instantiate the associated structure views + for (Entry entry : structureViewers + .entrySet()) { - createOrLinkStructureViewer(entry, af, ap); - } + createOrLinkStructureViewer(entry, af, ap); + } } /** @@ -3206,8 +3190,8 @@ public class Jalview2XML * @param viewerData * @param af */ - protected void createChimeraViewer(Entry viewerData, - AlignFrame af) + protected void createChimeraViewer( + Entry viewerData, AlignFrame af) { final StructureViewerModel data = viewerData.getValue(); String chimeraSession = data.getStateData(); @@ -3232,12 +3216,11 @@ public class Jalview2XML boolean colourBySequence = data.isColourWithAlignPanel(); // TODO can/should this be done via StructureViewer (like Jmol)? - final PDBEntry[] pdbArray = pdbs.toArray(new PDBEntry[pdbs - .size()]); - final SequenceI[][] seqsArray = allseqs.toArray(new SequenceI[allseqs.size()][]); + final PDBEntry[] pdbArray = pdbs.toArray(new PDBEntry[pdbs.size()]); + final SequenceI[][] seqsArray = allseqs.toArray(new SequenceI[allseqs + .size()][]); new ChimeraViewFrame(chimeraSession, af.alignPanel, pdbArray, - seqsArray, - colourByChimera, colourBySequence); + seqsArray, colourByChimera, colourBySequence); } else { @@ -3255,7 +3238,8 @@ public class Jalview2XML * @param af */ protected void createJmolViewer( - final Entry viewerData, AlignFrame af) + final Entry viewerData, + AlignFrame af) { final StructureViewerModel svattrib = viewerData.getValue(); String state = svattrib.getStateData(); @@ -3282,8 +3266,7 @@ public class Jalview2XML newFileLoc.append(Platform.escapeString(filedat.getFilePath())); pdbfilenames.add(filedat.getFilePath()); pdbids.add(filedat.getPdbId()); - seqmaps.add(filedat.getSeqList() - .toArray(new SequenceI[0])); + seqmaps.add(filedat.getSeqList().toArray(new SequenceI[0])); newFileLoc.append("\""); cp = ecp + 1; // advance beyond last \" and set cursor so we can // look for next file statement. @@ -3307,8 +3290,7 @@ public class Jalview2XML newFileLoc.append(filedat.getFilePath()); pdbfilenames.add(filedat.getFilePath()); pdbids.add(filedat.getPdbId()); - seqmaps.add(filedat.getSeqList() - .toArray(new SequenceI[0])); + seqmaps.add(filedat.getSeqList().toArray(new SequenceI[0])); newFileLoc.append(" \""); newFileLoc.append(filedat.getFilePath()); newFileLoc.append("\""); @@ -3414,8 +3396,8 @@ public class Jalview2XML * Post jalview 2.4 schema includes structure view id */ if (sviewid != null - && ((StructureViewerBase) frame).getViewId().equals( - sviewid)) + && ((StructureViewerBase) frame).getViewId() + .equals(sviewid)) { comp = (AppJmol) frame; // todo: break? @@ -3628,8 +3610,8 @@ public class Jalview2XML 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 { @@ -4245,9 +4227,8 @@ public class Jalview2XML { // JBP TODO: Check this is called for AlCodonFrames to support recovery of // xRef Codon Maps - jalview.datamodel.Sequence sq = (jalview.datamodel.Sequence) seqRefIds - .get(vamsasSeq.getId()); - jalview.datamodel.SequenceI dsq = null; + SequenceI sq = seqRefIds.get(vamsasSeq.getId()); + SequenceI dsq = null; if (sq != null && sq.getDatasetSequence() != null) { dsq = sq.getDatasetSequence(); @@ -4472,14 +4453,14 @@ public class Jalview2XML * local sequence definition */ Sequence ms = mc.getSequence(); - jalview.datamodel.Sequence djs = null; + SequenceI djs = null; String sqid = ms.getDsseqid(); if (sqid != null && sqid.length() > 0) { /* * recover dataset sequence */ - djs = (jalview.datamodel.Sequence) seqRefIds.get(sqid); + djs = seqRefIds.get(sqid); } else { diff --git a/src/jalview/gui/JvSwingUtils.java b/src/jalview/gui/JvSwingUtils.java index b4e0e00..1cc8a92 100644 --- a/src/jalview/gui/JvSwingUtils.java +++ b/src/jalview/gui/JvSwingUtils.java @@ -34,6 +34,7 @@ import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JPanel; +import javax.swing.JScrollBar; import javax.swing.SwingConstants; /** @@ -212,6 +213,53 @@ public final class JvSwingUtils } } + /** + * Returns the proportion of its range that a scrollbar's position represents, + * as a value between 0 and 1. For example if the whole range is from 0 to + * 200, then a position of 40 gives proportion = 0.2. + * + * @see http://www.javalobby.org/java/forums/t33050.html#91885334 + * + * @param scroll + * @return + */ + public static float getScrollBarProportion(JScrollBar scroll) + { + /* + * The extent (scroll handle width) deduction gives the true operating range + * of possible positions. + */ + int possibleRange = scroll.getMaximum() - scroll.getMinimum() + - scroll.getModel().getExtent(); + float valueInRange = scroll.getValue() + - (scroll.getModel().getExtent() / 2f); + float proportion = valueInRange / possibleRange; + return proportion; + } + + /** + * Returns the scroll bar position in its range that would match the given + * proportion (between 0 and 1) of the whole. For example if the whole range + * is from 0 to 200, then a proportion of 0.25 gives position 50. + * + * @param scrollbar + * @param proportion + * @return + */ + public static int getScrollValueForProportion(JScrollBar scrollbar, + float proportion) + { + /* + * The extent (scroll handle width) deduction gives the true operating range + * of possible positions. + */ + float fraction = proportion + * (scrollbar.getMaximum() - scrollbar.getMinimum() - scrollbar + .getModel().getExtent()) + + (scrollbar.getModel().getExtent() / 2f); + return Math.min(Math.round(fraction), scrollbar.getMaximum()); + } + public static void jvInitComponent(AbstractButton comp, String i18nString) { setColorAndFont(comp); diff --git a/src/jalview/gui/PCAPanel.java b/src/jalview/gui/PCAPanel.java index d2d6a98..412c25a 100644 --- a/src/jalview/gui/PCAPanel.java +++ b/src/jalview/gui/PCAPanel.java @@ -28,6 +28,7 @@ import jalview.datamodel.SequenceI; import jalview.jbgui.GPCAPanel; import jalview.schemes.ResidueProperties; import jalview.util.MessageManager; +import jalview.viewmodel.AlignmentViewport; import jalview.viewmodel.PCAModel; import java.awt.BorderLayout; @@ -67,7 +68,7 @@ public class PCAPanel extends GPCAPanel implements Runnable, AlignmentPanel ap; - AlignViewport av; + AlignmentViewport av; PCAModel pcaModel; diff --git a/src/jalview/gui/PaintRefresher.java b/src/jalview/gui/PaintRefresher.java index 01dfa3b..215090b 100755 --- a/src/jalview/gui/PaintRefresher.java +++ b/src/jalview/gui/PaintRefresher.java @@ -20,12 +20,15 @@ */ package jalview.gui; -import java.util.*; -import java.util.List; - -import java.awt.*; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.SequenceI; -import jalview.datamodel.*; +import java.awt.Component; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; /** * Route datamodel/view update events for a sequence set to any display @@ -36,57 +39,61 @@ import jalview.datamodel.*; */ public class PaintRefresher { - static Hashtable components; + static Map> components = new HashMap>(); /** - * DOCUMENT ME! + * Add the given component to those registered under the given sequence set + * id. Does nothing if already added. * * @param comp - * DOCUMENT ME! * @param al - * DOCUMENT ME! */ public static void Register(Component comp, String seqSetId) { - if (components == null) - { - components = new Hashtable(); - } - if (components.containsKey(seqSetId)) { - Vector comps = (Vector) components.get(seqSetId); + List comps = components.get(seqSetId); if (!comps.contains(comp)) { - comps.addElement(comp); + comps.add(comp); } } else { - Vector vcoms = new Vector(); - vcoms.addElement(comp); + List vcoms = new ArrayList(); + vcoms.add(comp); components.put(seqSetId, vcoms); } } + /** + * Remove this component from all registrations. Also removes a registered + * sequence set id if there are no remaining components registered against it. + * + * @param comp + */ public static void RemoveComponent(Component comp) { - if (components == null) - { - return; - } - - Enumeration en = components.keys(); - while (en.hasMoreElements()) + List emptied = new ArrayList(); + for (Entry> registered : components.entrySet()) { - String id = en.nextElement().toString(); - Vector comps = (Vector) components.get(id); + String id = registered.getKey(); + List comps = components.get(id); comps.remove(comp); - if (comps.size() == 0) + if (comps.isEmpty()) { - components.remove(id); + emptied.add(id); } } + + /* + * Remove now empty ids after the above (to avoid + * ConcurrentModificationException). + */ + for (String id : emptied) + { + components.remove(id); + } } public static void Refresh(Component source, String id) @@ -97,24 +104,15 @@ public class PaintRefresher public static void Refresh(Component source, String id, boolean alignmentChanged, boolean validateSequences) { - if (components == null) - { - return; - } - - Component comp; - Vector comps = (Vector) components.get(id); + List comps = components.get(id); if (comps == null) { return; } - Enumeration e = comps.elements(); - while (e.hasMoreElements()) + for (Component comp : comps) { - comp = (Component) e.nextElement(); - if (comp == source) { continue; @@ -242,30 +240,20 @@ public class PaintRefresher static AlignmentPanel[] getAssociatedPanels(String id) { - if (components == null) - { - return new AlignmentPanel[0]; - } - ; - Vector comps = (Vector) components.get(id); + List comps = components.get(id); if (comps == null) { return new AlignmentPanel[0]; } - ; - Vector tmp = new Vector(); - int i, iSize = comps.size(); - for (i = 0; i < iSize; i++) + List tmp = new ArrayList(); + for (Component comp : comps) { - if (comps.elementAt(i) instanceof AlignmentPanel) + if (comp instanceof AlignmentPanel) { - tmp.addElement(comps.elementAt(i)); + tmp.add((AlignmentPanel) comp); } } - AlignmentPanel[] result = new AlignmentPanel[tmp.size()]; - tmp.toArray(result); - - return result; + return tmp.toArray(new AlignmentPanel[tmp.size()]); } } diff --git a/src/jalview/gui/PairwiseAlignPanel.java b/src/jalview/gui/PairwiseAlignPanel.java index bc2c27c..29d6b71 100755 --- a/src/jalview/gui/PairwiseAlignPanel.java +++ b/src/jalview/gui/PairwiseAlignPanel.java @@ -26,6 +26,7 @@ import jalview.datamodel.Sequence; import jalview.datamodel.SequenceI; import jalview.jbgui.GPairwiseAlignPanel; import jalview.util.MessageManager; +import jalview.viewmodel.AlignmentViewport; import java.awt.event.ActionEvent; import java.util.Vector; @@ -39,7 +40,7 @@ import java.util.Vector; public class PairwiseAlignPanel extends GPairwiseAlignPanel { - AlignViewport av; + AlignmentViewport av; Vector sequences; @@ -49,7 +50,7 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel * @param av * DOCUMENT ME! */ - public PairwiseAlignPanel(AlignViewport av) + public PairwiseAlignPanel(AlignmentViewport av) { super(); this.av = av; diff --git a/src/jalview/gui/PopupMenu.java b/src/jalview/gui/PopupMenu.java index 8565f9f..638962c 100644 --- a/src/jalview/gui/PopupMenu.java +++ b/src/jalview/gui/PopupMenu.java @@ -2548,15 +2548,7 @@ public class PopupMenu extends JPopupMenu } int gsize = sg.getSize(); - SequenceI[] hseqs; - - hseqs = new SequenceI[gsize]; - - int index = 0; - for (int i = 0; i < gsize; i++) - { - hseqs[index++] = sg.getSequenceAt(i); - } + SequenceI[] hseqs = sg.getSequences().toArray(new SequenceI[gsize]); ap.av.hideSequence(hseqs); // refresh(); TODO: ? needed ? diff --git a/src/jalview/gui/RedundancyPanel.java b/src/jalview/gui/RedundancyPanel.java index c8a0ec7..1b5695c 100755 --- a/src/jalview/gui/RedundancyPanel.java +++ b/src/jalview/gui/RedundancyPanel.java @@ -276,10 +276,10 @@ public class RedundancyPanel extends GSliderPanel implements Runnable } 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(); } diff --git a/src/jalview/gui/RotatableCanvas.java b/src/jalview/gui/RotatableCanvas.java index 1ff78b4..6aff578 100755 --- a/src/jalview/gui/RotatableCanvas.java +++ b/src/jalview/gui/RotatableCanvas.java @@ -30,6 +30,7 @@ import jalview.api.RotatableCanvasI; import jalview.datamodel.*; import jalview.math.*; import jalview.util.MessageManager; +import jalview.viewmodel.AlignmentViewport; /** * DOCUMENT ME! @@ -101,7 +102,7 @@ public class RotatableCanvas extends JPanel implements MouseListener, float scalefactor = 1; - AlignViewport av; + AlignmentViewport av; AlignmentPanel ap; diff --git a/src/jalview/gui/ScalePanel.java b/src/jalview/gui/ScalePanel.java index 32773b9..754b763 100755 --- a/src/jalview/gui/ScalePanel.java +++ b/src/jalview/gui/ScalePanel.java @@ -478,20 +478,16 @@ public class ScalePanel extends JPanel implements MouseMotionListener, maxX = (i - startx + 1) * av.charWidth + fm.stringWidth(string); } - gg.drawLine( - ((i - startx - 1) * av.charWidth) + (av.charWidth / 2), - y + 2, - ((i - startx - 1) * av.charWidth) + (av.charWidth / 2), - y + (fm.getDescent() * 2)); + gg.drawLine(((i - startx - 1) * av.charWidth) + (av.charWidth / 2), + y + 2, ((i - startx - 1) * av.charWidth) + + (av.charWidth / 2), y + (fm.getDescent() * 2)); } else { - gg.drawLine( - ((i - startx - 1) * av.charWidth) + (av.charWidth / 2), - y + fm.getDescent(), - ((i - startx - 1) * av.charWidth) + (av.charWidth / 2), - y + (fm.getDescent() * 2)); + gg.drawLine(((i - startx - 1) * av.charWidth) + (av.charWidth / 2), + y + fm.getDescent(), ((i - startx - 1) * av.charWidth) + + (av.charWidth / 2), y + (fm.getDescent() * 2)); } } diff --git a/src/jalview/gui/SeqCanvas.java b/src/jalview/gui/SeqCanvas.java index 5032f69..38aac43 100755 --- a/src/jalview/gui/SeqCanvas.java +++ b/src/jalview/gui/SeqCanvas.java @@ -644,9 +644,8 @@ public class SeqCanvas extends JComponent int blockStart = startRes; int blockEnd = endRes; - for (int i = 0; regions != null && i < regions.size(); i++) + for (int[] region : regions) { - int[] region = regions.get(i); int hideStart = region[0]; int hideEnd = region[1]; diff --git a/src/jalview/gui/SeqPanel.java b/src/jalview/gui/SeqPanel.java index 7c6a202..f52f926 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -20,8 +20,10 @@ */ package jalview.gui; +import jalview.api.AlignViewportI; 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; @@ -34,7 +36,11 @@ import jalview.structure.SelectionListener; import jalview.structure.SelectionSource; import jalview.structure.SequenceListener; import jalview.structure.StructureSelectionManager; +import jalview.structure.VamsasSource; +import jalview.util.Comparison; +import jalview.util.MappingUtils; import jalview.util.MessageManager; +import jalview.viewmodel.AlignmentViewport; import java.awt.BorderLayout; import java.awt.Color; @@ -243,22 +249,33 @@ public class SeqPanel extends JPanel implements MouseListener, return seq; } + /** + * 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() @@ -638,6 +655,11 @@ public class SeqPanel extends JPanel implements MouseListener, } @Override + public VamsasSource getVamsasSource() + { + return this.ap == null ? null : this.ap.av; + } + @Override public void updateColours(SequenceI seq, int index) { System.out.println("update the seqPanel colours"); @@ -775,35 +797,36 @@ public class SeqPanel extends JPanel implements MouseListener, int setStatusMessage(SequenceI sequence, int res, int seq) { int pos = -1; - StringBuffer text = new StringBuffer("Sequence " + (seq + 1) + " ID: " - + sequence.getName()); + StringBuilder text = new StringBuilder(32); + text.append("Sequence " + (seq + 1) + " ID: " + sequence.getName()); - Object obj = null; + String residue = null; + /* + * Try to translate the display character to residue name (null for gap). + */ + final String displayChar = String.valueOf(sequence.getCharAt(res)); if (av.getAlignment().isNucleotide()) { - obj = ResidueProperties.nucleotideName.get(sequence.getCharAt(res) - + ""); - if (obj != null) + residue = ResidueProperties.nucleotideName.get(displayChar); + if (residue != null) { - text.append(" Nucleotide: "); + text.append(" Nucleotide: ").append(residue); } } else { - obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + ""); - if (obj != null) + residue = "X".equalsIgnoreCase(displayChar) ? "STOP" + : ResidueProperties.aa2Triplet.get(displayChar); + if (residue != null) { - text.append(" Residue: "); + text.append(" Residue: ").append(residue); } } - if (obj != null) + if (residue != null) { pos = sequence.findPosition(res); - if (obj != "") - { - text.append(obj + " (" + pos + ")"); - } + text.append(" (").append(Integer.toString(pos)).append(")"); } ap.alignFrame.statusBar.setText(text.toString()); return pos; @@ -924,7 +947,7 @@ public class SeqPanel extends JPanel implements MouseListener, } } - StringBuffer message = new StringBuffer(); + StringBuilder message = new StringBuilder(64); if (groupEditing) { message.append("Edit group:"); @@ -1149,8 +1172,8 @@ public class SeqPanel extends JPanel implements MouseListener, } else { - editCommand.appendEdit(Action.INSERT_GAP, groupSeqs, - startres, startres - lastres, av.getAlignment(), true); + appendEdit(Action.INSERT_GAP, groupSeqs, startres, startres + - lastres); } } else @@ -1165,8 +1188,8 @@ public class SeqPanel extends JPanel implements MouseListener, } else { - editCommand.appendEdit(Action.DELETE_GAP, groupSeqs, - startres, lastres - startres, av.getAlignment(), true); + appendEdit(Action.DELETE_GAP, groupSeqs, startres, lastres + - startres); } } @@ -1187,8 +1210,8 @@ public class SeqPanel extends JPanel implements MouseListener, } 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 @@ -1200,7 +1223,7 @@ public class SeqPanel extends JPanel implements MouseListener, { for (int j = lastres; j > startres; j--) { - if (!jalview.util.Comparison.isGap(seq.getCharAt(startres))) + if (!Comparison.isGap(seq.getCharAt(startres))) { endEditing(); break; @@ -1215,7 +1238,7 @@ public class SeqPanel extends JPanel implements MouseListener, 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; } @@ -1224,9 +1247,8 @@ public class SeqPanel extends JPanel implements MouseListener, 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); } } } @@ -1242,8 +1264,8 @@ public class SeqPanel extends JPanel implements MouseListener, } 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); } } } @@ -1278,22 +1300,37 @@ public class SeqPanel extends JPanel implements MouseListener, } } - editCommand.appendEdit(Action.DELETE_GAP, seq, blankColumn, 1, - av.getAlignment(), true); + appendEdit(Action.DELETE_GAP, seq, blankColumn, 1); - editCommand.appendEdit(Action.INSERT_GAP, seq, j, 1, - av.getAlignment(), true); + 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) + { + + 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); } /** @@ -1799,52 +1836,68 @@ public class SeqPanel extends JPanel implements MouseListener, // handles selection messages... // TODO: extend config options to allow user to control if selections may be // shared between viewports. - if (av == source - || !av.followSelection - || (av.isSelectionGroupChanged(false) || av - .isColSelChanged(false)) - || (source instanceof AlignViewport && ((AlignViewport) source) - .getSequenceSetId().equals(av.getSequenceSetId()))) + boolean iSentTheSelection = (av == source + || (source instanceof AlignViewport && ((AlignmentViewport) source) + .getSequenceSetId().equals(av.getSequenceSetId()))); + if (iSentTheSelection || !av.followSelection) + { + return; + } + + /* + * Ignore the selection if there is one of our own pending. + */ + if (av.isSelectionGroupChanged(false) || av.isColSelChanged(false)) + { + return; + } + + /* + * Check for selection in a view of which this one is a dna/protein + * complement. + */ + if (selectionFromTranslation(seqsel, colsel, source)) { return; } + // do we want to thread this ? (contention with seqsel and colsel locks, I // suspect) // rules are: colsel is copied if there is a real intersection between // sequence selection - boolean repaint = false, copycolsel = true; - // if (!av.isSelectionGroupChanged(false)) + boolean repaint = false; + boolean copycolsel = true; + + SequenceGroup sgroup = null; + if (seqsel != null && seqsel.getSize() > 0) { - SequenceGroup sgroup = null; - if (seqsel != null && seqsel.getSize() > 0) + if (av.getAlignment() == null) { - if (av.getAlignment() == null) - { - jalview.bin.Cache.log.warn("alignviewport av SeqSetId=" - + av.getSequenceSetId() + " ViewId=" + av.getViewId() - + " 's alignment is NULL! returning immediatly."); - return; - } - sgroup = seqsel.intersect(av.getAlignment(), - (av.hasHiddenRows()) ? av.getHiddenRepSequences() : null); - if ((sgroup == null || sgroup.getSize() == 0) - || (colsel == null || colsel.size() == 0)) - { - // don't copy columns if the region didn't intersect. - copycolsel = false; - } + jalview.bin.Cache.log.warn("alignviewport av SeqSetId=" + + av.getSequenceSetId() + " ViewId=" + av.getViewId() + + " 's alignment is NULL! returning immediately."); + return; } - if (sgroup != null && sgroup.getSize() > 0) + sgroup = seqsel.intersect(av.getAlignment(), + (av.hasHiddenRows()) ? av.getHiddenRepSequences() : null); + if ((sgroup == null || sgroup.getSize() == 0) + || (colsel == null || colsel.size() == 0)) { - av.setSelectionGroup(sgroup); + // don't copy columns if the region didn't intersect. + copycolsel = false; } - else - { - av.setSelectionGroup(null); - } - av.isSelectionGroupChanged(true); - repaint = true; } + if (sgroup != null && sgroup.getSize() > 0) + { + av.setSelectionGroup(sgroup); + } + else + { + av.setSelectionGroup(null); + } + av.isSelectionGroupChanged(true); + repaint = true; + if (copycolsel) { // the current selection is unset or from a previous message @@ -1872,6 +1925,7 @@ public class SeqPanel extends JPanel implements MouseListener, av.isColSelChanged(true); repaint = true; } + if (copycolsel && av.hasHiddenColumns() && (av.getColumnSelection() == null || av.getColumnSelection() @@ -1879,11 +1933,52 @@ public class SeqPanel extends JPanel implements MouseListener, { System.err.println("Bad things"); } - if (repaint) + if (repaint) // always true! { // probably finessing with multiple redraws here PaintRefresher.Refresh(this, av.getSequenceSetId()); // ap.paintAlignment(false); } } + + /** + * If this panel is a cdna/protein translation view of the selection source, + * tries to map the source selection to a local one, and returns true. Else + * returns false. + * + * @param seqsel + * @param colsel + * @param source + */ + protected boolean selectionFromTranslation(SequenceGroup seqsel, + ColumnSelection colsel, SelectionSource source) + { + if (!(source instanceof AlignViewportI)) { + return false; + } + final AlignViewportI sourceAv = (AlignViewportI) source; + if (sourceAv.getCodingComplement() != av && av.getCodingComplement() != sourceAv) + { + return false; + } + + /* + * Map sequence selection + */ + SequenceGroup sg = MappingUtils.mapSequenceGroup(seqsel, sourceAv, av); + av.setSelectionGroup(sg); + av.isSelectionGroupChanged(true); + + /* + * Map column selection + */ + ColumnSelection cs = MappingUtils.mapColumnSelection(colsel, sourceAv, + av); + av.setColumnSelection(cs); + av.isColSelChanged(true); + + PaintRefresher.Refresh(this, av.getSequenceSetId()); + + return true; + } } diff --git a/src/jalview/gui/SequenceFetcher.java b/src/jalview/gui/SequenceFetcher.java index 35bc29a..e159d41 100755 --- a/src/jalview/gui/SequenceFetcher.java +++ b/src/jalview/gui/SequenceFetcher.java @@ -20,24 +20,42 @@ */ package jalview.gui; -import java.util.*; -import java.util.List; - -import java.awt.*; -import java.awt.event.*; - -import javax.swing.*; -import javax.swing.tree.DefaultMutableTreeNode; - -import com.stevesoft.pat.Regex; - -import jalview.datamodel.*; -import jalview.io.*; +import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.DBRefEntry; +import jalview.datamodel.DBRefSource; +import jalview.datamodel.SequenceFeature; +import jalview.datamodel.SequenceI; +import jalview.io.FormatAdapter; +import jalview.io.IdentifyFile; import jalview.util.DBRefUtils; import jalview.util.MessageManager; import jalview.ws.dbsources.das.api.DasSourceRegistryI; import jalview.ws.seqfetcher.DbSourceProxy; + import java.awt.BorderLayout; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JInternalFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.SwingConstants; +import javax.swing.tree.DefaultMutableTreeNode; + +import com.stevesoft.pat.Regex; public class SequenceFetcher extends JPanel implements Runnable { @@ -283,7 +301,9 @@ public class SequenceFetcher extends JPanel implements Runnable public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) + { ok_actionPerformed(); + } } }); jPanel3.setLayout(borderLayout1); @@ -821,21 +841,7 @@ public class SequenceFetcher extends JPanel implements Runnable } else { - for (int i = 0; i < al.getHeight(); i++) - { - alignFrame.viewport.getAlignment().addSequence( - al.getSequenceAt(i)); // this - // also - // creates - // dataset - // sequence - // entries - } - alignFrame.viewport.setEndSeq(alignFrame.viewport.getAlignment() - .getHeight()); - alignFrame.viewport.getAlignment().getWidth(); - alignFrame.viewport.firePropertyChange("alignment", null, - alignFrame.viewport.getAlignment().getSequences()); + alignFrame.viewport.addAlignment(al, title); } } return al; diff --git a/src/jalview/gui/SplitFrame.java b/src/jalview/gui/SplitFrame.java new file mode 100644 index 0000000..f98eea7 --- /dev/null +++ b/src/jalview/gui/SplitFrame.java @@ -0,0 +1,219 @@ +package jalview.gui; + +import jalview.jbgui.GSplitFrame; + +import java.awt.Component; +import java.awt.MouseInfo; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.util.Map.Entry; + +import javax.swing.AbstractAction; +import javax.swing.JComponent; +import javax.swing.JMenuItem; +import javax.swing.KeyStroke; +import javax.swing.event.InternalFrameAdapter; +import javax.swing.event.InternalFrameEvent; + +public class SplitFrame extends GSplitFrame +{ + private static final long serialVersionUID = 1L; + + public SplitFrame(JComponent top, JComponent bottom) + { + super(top, bottom); + init(); + } + + /** + * Initialise this frame. + */ + protected void init() + { + setSize(AlignFrame.DEFAULT_WIDTH, Desktop.instance.getHeight() - 20); + + addCloseFrameListener(); + + addKeyListener(); + + addKeyBindings(); + + } + + /** + * Add a listener to tidy up when the frame is closed. + */ + protected void addCloseFrameListener() + { + addInternalFrameListener(new InternalFrameAdapter() + { + @Override + public void internalFrameClosed(InternalFrameEvent evt) + { + if (getTopComponent() instanceof AlignFrame) + { + ((AlignFrame) getTopComponent()) + .closeMenuItem_actionPerformed(true); + } + if (getBottomComponent() instanceof AlignFrame) + { + ((AlignFrame) getBottomComponent()) + .closeMenuItem_actionPerformed(true); + } + }; + }); + } + + /** + * Add a key listener that delegates to whichever split component the mouse is + * in (or does nothing if neither). + */ + protected void addKeyListener() + { + // TODO Key Bindings rather than KeyListener are recommended for Swing + addKeyListener(new KeyAdapter() { + + @Override + public void keyPressed(KeyEvent e) + { + Component c = getComponentAtMouse(); + if (c != null) + { + for (KeyListener kl : c.getKeyListeners()) + { + kl.keyPressed(e); + } + } + } + + @Override + public void keyReleased(KeyEvent e) + { + Component c = getComponentAtMouse(); + if (c != null) + { + for (KeyListener kl : c.getKeyListeners()) + { + kl.keyReleased(e); + } + } + } + + }); + } + + /** + * Returns the split pane component the mouse is in, or null if neither. + * + * @return + */ + protected Component getComponentAtMouse() + { + Point loc = MouseInfo.getPointerInfo().getLocation(); + + if (isIn(loc, getTopComponent())) { + return getTopComponent(); + } + else if (isIn(loc, getBottomComponent())) + { + return getBottomComponent(); + } + return null; + } + + private boolean isIn(Point loc, JComponent comp) + { + Point p = comp.getLocationOnScreen(); + Rectangle r = new Rectangle(p.x, p.y, comp.getWidth(), comp.getHeight()); + return r.contains(loc); + } + + /** + * Set key bindings (recommended for Swing over key accelerators). For now, + * delegate to the corresponding key accelerator for the AlignFrame that the + * mouse is in. Hopefully can be simplified in future if AlignFrame is changed + * to use key bindings rather than accelerators. + */ + private void addKeyBindings() + { + if (getTopComponent() instanceof AlignFrame) + { + for (Entry acc : ((AlignFrame) getTopComponent()) + .getAccelerators().entrySet()) + { + + final KeyStroke ks = acc.getKey(); + this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ks, ks); + this.getActionMap().put(ks, new AbstractAction() + { + @Override + public void actionPerformed(ActionEvent e) + { + Component c = getComponentAtMouse(); + if (c instanceof AlignFrame) + { + for (ActionListener a : ((AlignFrame) c).getAccelerators() + .get(ks).getActionListeners()) + { + + a.actionPerformed(null); + } + } + } + }); + } + /* + * Disable unwanted here + */ + // X expand views - wrecks the split pane view + KeyStroke key_X = KeyStroke.getKeyStroke(KeyEvent.VK_X, 0, false); + disableAccelerator(key_X); + } + } + + /** + * Ugly hack for Proof of Concept that disables the key binding in this frame + * _and_ disables the bound menu item _and_ removes the key accelerator in the + * child frames. + * + * @param key + */ + protected void disableAccelerator(KeyStroke key) + { + disableAccelerator(key, getTopComponent()); + disableAccelerator(key, getBottomComponent()); + this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).remove(key); + } + + /** + * Disable the menu item for which this key is the accelerator, also removes + * its action listeners to prevent key accelerator working. + * + * @param key + * @param comp + */ + private void disableAccelerator(KeyStroke key, JComponent comp) + { + // HACKED ONLY FOR PROOF OF CONCEPT + // Proper solution might involve explicit 'configure menu' method on + // AlignFrame, or + // changing key listeners to key bindings in AlignFrame, or both + if (comp instanceof AlignFrame) + { + JMenuItem mi = ((AlignFrame) comp).getAccelerators().get(key); + if (mi != null) + { + mi.setEnabled(false); + for (ActionListener al : mi.getActionListeners()) + { + mi.removeActionListener(al); + } + } + } + } +} diff --git a/src/jalview/gui/TreeCanvas.java b/src/jalview/gui/TreeCanvas.java index c5650f5..e90f867 100755 --- a/src/jalview/gui/TreeCanvas.java +++ b/src/jalview/gui/TreeCanvas.java @@ -20,17 +20,47 @@ */ package jalview.gui; -import java.util.*; - -import java.awt.*; -import java.awt.event.*; -import java.awt.print.*; -import javax.swing.*; - -import jalview.analysis.*; -import jalview.datamodel.*; -import jalview.schemes.*; -import jalview.util.*; +import jalview.analysis.Conservation; +import jalview.analysis.NJTree; +import jalview.api.AlignViewportI; +import jalview.datamodel.Sequence; +import jalview.datamodel.SequenceGroup; +import jalview.datamodel.SequenceI; +import jalview.datamodel.SequenceNode; +import jalview.schemes.ColourSchemeI; +import jalview.schemes.ColourSchemeProperty; +import jalview.schemes.ResidueProperties; +import jalview.schemes.UserColourScheme; +import jalview.structure.SelectionSource; +import jalview.util.Format; +import jalview.util.MappingUtils; +import jalview.util.MessageManager; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterJob; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import javax.swing.JColorChooser; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; +import javax.swing.ToolTipManager; /** * DOCUMENT ME! @@ -39,7 +69,7 @@ import jalview.util.*; * @version $Revision$ */ public class TreeCanvas extends JPanel implements MouseListener, Runnable, - Printable, MouseMotionListener + Printable, MouseMotionListener, SelectionSource { /** DOCUMENT ME!! */ public static final String PLACEHOLDER = " * "; @@ -208,7 +238,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, if (node.element() instanceof SequenceI) { - SequenceI seq = (SequenceI) ((SequenceNode) node).element(); + SequenceI seq = (SequenceI) node.element(); if (av.getSequenceColour(seq) == Color.white) { @@ -258,14 +288,14 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, Rectangle rect = new Rectangle(xend + 10, ypos - charHeight / 2, charWidth, charHeight); - nameHash.put((SequenceI) node.element(), rect); + nameHash.put(node.element(), rect); // Colour selected leaves differently SequenceGroup selected = av.getSelectionGroup(); if ((selected != null) && selected.getSequences(null).contains( - (SequenceI) node.element())) + node.element())) { g.setColor(Color.gray); @@ -290,7 +320,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, int xend = (int) (height * scale) + offx; int ypos = (int) (node.ycount * chunk) + offy; - g.setColor(((SequenceNode) node).color.darker()); + g.setColor(node.color.darker()); // Draw horizontal line g.drawLine(xstart, ypos, xend, ypos); @@ -493,7 +523,8 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, { for (int a = 0; a < aps.length; a++) { - aps[a].av.setSequenceColour((SequenceI) node.element(), c); + final SequenceI seq = (SequenceI) node.element(); + aps[a].av.setSequenceColour(seq, c); } } } @@ -682,7 +713,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, labelLength = fm.stringWidth(longestName) + 20; // 20 allows for scrollbar - float wscale = (float) (width - labelLength - (offx * 2)) + float wscale = (width - labelLength - (offx * 2)) / tree.getMaxHeight(); SequenceNode top = tree.getTopNode(); @@ -708,7 +739,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, g2.setColor(Color.gray); } - int x = (int) ((threshold * (float) (getWidth() - labelLength - (2 * offx))) + offx); + int x = (int) ((threshold * (getWidth() - labelLength - (2 * offx))) + offx); g2.drawLine(x, 0, x, getHeight()); } @@ -854,12 +885,20 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, AlignmentPanel[] aps = getAssociatedPanels(); + // TODO push calls below into a single AlignViewportI method? + // see also AlignViewController.deleteGroups for (int a = 0; a < aps.length; a++) { aps[a].av.setSelectionGroup(null); aps[a].av.getAlignment().deleteAllGroups(); aps[a].av.clearSequenceColours(); } + if (av.getCodingComplement() != null) + { + av.getCodingComplement().setSelectionGroup(null); + av.getCodingComplement().getAlignment().deleteAllGroups(); + av.getCodingComplement().clearSequenceColours(); + } colourGroups(); } @@ -923,6 +962,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, // sg.recalcConservation(); sg.setName("JTreeGroup:" + sg.hashCode()); sg.setIdColour(col); + for (int a = 0; a < aps.length; a++) { if (aps[a].av.getGlobalColourScheme() != null @@ -939,7 +979,26 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, aps[a].av.getAlignment().addGroup(new SequenceGroup(sg)); } + + // TODO can we push all of the below into AlignViewportI? + av.getAlignment().addGroup(sg); + final AlignViewportI codingComplement = av.getCodingComplement(); + if (codingComplement != null) + { + SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(sg, av, + codingComplement); + if (mappedGroup.getSequences().size() > 0) + { + codingComplement.getAlignment().addGroup(mappedGroup); + for (SequenceI seq : mappedGroup.getSequences()) + { + codingComplement.setSequenceColour(seq, + mappedGroup.getIdColour().brighter()); + } + } + } } + // notify the panel to redo any group specific stuff. for (int a = 0; a < aps.length; a++) { @@ -948,6 +1007,13 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, // to any Jmols listening in } + if (av.getCodingComplement() != null) + { + ((AlignViewport) av.getCodingComplement()).getAlignPanel().updateAnnotation(); + /* + * idPanel. repaint () + */ + } } /** diff --git a/src/jalview/gui/TreePanel.java b/src/jalview/gui/TreePanel.java index 6ce68b3..d7809cb 100755 --- a/src/jalview/gui/TreePanel.java +++ b/src/jalview/gui/TreePanel.java @@ -242,7 +242,8 @@ public class TreePanel extends GTreePanel associateLeavesMenu.add(item); } - final JRadioButtonMenuItem itemf = new JRadioButtonMenuItem("All Views"); + final JRadioButtonMenuItem itemf = new JRadioButtonMenuItem( + "label.all_views"); buttonGroup.add(itemf); itemf.setSelected(treeCanvas.applyToAllViews); itemf.addActionListener(new ActionListener() @@ -343,7 +344,7 @@ public class TreePanel extends GTreePanel av.setCurrentTree(tree); if (av.getSortByTree()) { - sortByTree_actionPerformed(null); + sortByTree_actionPerformed(); } } } @@ -531,7 +532,7 @@ public class TreePanel extends GTreePanel // msaorder); Desktop.addInternalFrame(af, MessageManager.formatMessage( - "label.original_data_for_params", new String[] + "label.original_data_for_params", new Object[] { this.title }), AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); } @@ -555,7 +556,8 @@ public class TreePanel extends GTreePanel * * @param e */ - public void sortByTree_actionPerformed(ActionEvent e) + @Override + public void sortByTree_actionPerformed() { if (treeCanvas.applyToAllViews) diff --git a/src/jalview/gui/VamsasApplication.java b/src/jalview/gui/VamsasApplication.java index 77a7693..04cc9e3 100644 --- a/src/jalview/gui/VamsasApplication.java +++ b/src/jalview/gui/VamsasApplication.java @@ -32,12 +32,12 @@ import jalview.structure.StructureSelectionManager; import jalview.structure.VamsasListener; import jalview.structure.VamsasSource; import jalview.util.MessageManager; +import jalview.viewmodel.AlignmentViewport; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.io.IOException; -import java.util.Enumeration; import java.util.Hashtable; import java.util.IdentityHashMap; import java.util.Iterator; @@ -347,7 +347,9 @@ public class VamsasApplication implements SelectionSource, VamsasSource 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 { @@ -958,11 +960,14 @@ public class VamsasApplication implements SelectionSource, VamsasSource 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); @@ -998,7 +1003,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource AlignmentI visal = null; if (source instanceof AlignViewport) { - visal = ((AlignViewport) source).getAlignment(); + visal = ((AlignmentViewport) source).getAlignment(); } SelectionMessage sm = null; if ((seqsel == null || seqsel.getSize() == 0) @@ -1009,7 +1014,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource { // the empty selection. sm = new SelectionMessage("jalview", new String[] - { ((AlignViewport) source).getSequenceSetId() }, null, + { ((AlignmentViewport) source).getSequenceSetId() }, null, true); } else @@ -1050,10 +1055,9 @@ public class VamsasApplication implements SelectionSource, VamsasSource { // gather selected columns outwith the sequence positions // too - Enumeration cols = colsel.getSelected().elements(); - while (cols.hasMoreElements()) + for (Object obj : colsel.getSelected()) { - int ival = ((Integer) cols.nextElement()).intValue(); + int ival = ((Integer) obj).intValue(); Pos p = new Pos(); p.setI(ival + 1); range.addPos(p); diff --git a/src/jalview/gui/WsJobParameters.java b/src/jalview/gui/WsJobParameters.java index 4a74c9c..0772781 100644 --- a/src/jalview/gui/WsJobParameters.java +++ b/src/jalview/gui/WsJobParameters.java @@ -1306,7 +1306,7 @@ public class WsJobParameters extends JPanel implements ItemListener, */ protected void updateWebServiceMenus() { - for (AlignFrame alignFrame : Desktop.getAlignframes()) + for (AlignFrame alignFrame : Desktop.getAlignFrames()) { alignFrame.BuildWebServiceMenu(); } diff --git a/src/jalview/io/AppletFormatAdapter.java b/src/jalview/io/AppletFormatAdapter.java index 89adead..f316a7e 100755 --- a/src/jalview/io/AppletFormatAdapter.java +++ b/src/jalview/io/AppletFormatAdapter.java @@ -46,10 +46,24 @@ public class AppletFormatAdapter * List of valid format strings used in the isValidFormat method */ public static final String[] READABLE_FORMATS = new String[] - { "BLC", "CLUSTAL", "FASTA", "MSF", "PileUp", "PIR", "PFAM", "STH", - "PDB", "JnetFile", "RNAML", PhylipFile.FILE_DESC, "HTML" }; // , - // "SimpleBLAST" - // }; + { "BLC", "CLUSTAL", "FASTA", "MSF", "PileUp", "PIR", "PFAM", "STH", + "PDB", "JnetFile", "RNAML", PhylipFile.FILE_DESC, "HTML" }; + + /** + * List of readable format file extensions by application in order + * corresponding to READABLE_FNAMES + */ + public static final String[] READABLE_EXTENSIONS = new String[] + { "fa, fasta, mfa, fastq", "aln", "pfam", "msf", "pir", "blc", "amsa", + "sto,stk", "xml,rnaml", PhylipFile.FILE_EXT, "jar,jvp", "html" }; + + /** + * List of readable formats by application in order corresponding to + * READABLE_EXTENSIONS + */ + public static final String[] READABLE_FNAMES = new String[] + { "Fasta", "Clustal", "PFAM", "MSF", "PIR", "BLC", "AMSA", "Stockholm", + "RNAML", PhylipFile.FILE_DESC, "Jalview", "HTML" }; /** * List of valid format strings for use by callers of the formatSequences @@ -75,26 +89,6 @@ public class AppletFormatAdapter { "Fasta", "Clustal", "PFAM", "MSF", "PIR", "BLC", "AMSA", "STH", PhylipFile.FILE_DESC, "Jalview" }; - /** - * List of readable format file extensions by application in order - * corresponding to READABLE_FNAMES - */ - public static final String[] READABLE_EXTENSIONS = new String[] - { "fa, fasta, mfa, fastq", "aln", "pfam", "msf", "pir", "blc", "amsa", - "jar,jvp", "sto,stk", "xml,rnaml", PhylipFile.FILE_EXT, - "html" }; // ".blast" - - /** - * List of readable formats by application in order corresponding to - * READABLE_EXTENSIONS - */ - public static final String[] READABLE_FNAMES = new String[] - { "Fasta", "Clustal", "PFAM", "MSF", "PIR", "BLC", "AMSA", "Jalview", - "Stockholm", "RNAML", PhylipFile.FILE_DESC, "HTML" };// , - - // "SimpleBLAST" - // }; - public static String INVALID_CHARACTERS = "Contains invalid characters"; // TODO: make these messages dynamic diff --git a/src/jalview/io/FileLoader.java b/src/jalview/io/FileLoader.java index 0285bf0..906e823 100755 --- a/src/jalview/io/FileLoader.java +++ b/src/jalview/io/FileLoader.java @@ -332,13 +332,7 @@ public class FileLoader implements Runnable } if (viewport != null) { - // TODO: create undo object for this JAL-1101 - for (int i = 0; i < al.getHeight(); i++) - { - viewport.getAlignment().addSequence(al.getSequenceAt(i)); - } - viewport.firePropertyChange("alignment", null, viewport - .getAlignment().getSequences()); + viewport.addAlignment(al, title); } else { diff --git a/src/jalview/io/VamsasAppDatastore.java b/src/jalview/io/VamsasAppDatastore.java index 90447db..1d36865 100644 --- a/src/jalview/io/VamsasAppDatastore.java +++ b/src/jalview/io/VamsasAppDatastore.java @@ -34,6 +34,7 @@ import jalview.io.vamsas.DatastoreItem; import jalview.io.vamsas.DatastoreRegistry; import jalview.io.vamsas.Rangetype; import jalview.util.MessageManager; +import jalview.viewmodel.AlignmentViewport; import java.io.IOException; import java.util.Enumeration; @@ -42,12 +43,35 @@ import java.util.Hashtable; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; +import java.util.Set; import java.util.Vector; import java.util.jar.JarInputStream; import java.util.jar.JarOutputStream; -import uk.ac.vamsas.client.*; -import uk.ac.vamsas.objects.core.*; +import uk.ac.vamsas.client.IClientAppdata; +import uk.ac.vamsas.client.IClientDocument; +import uk.ac.vamsas.client.Vobject; +import uk.ac.vamsas.client.VorbaId; +import uk.ac.vamsas.objects.core.Alignment; +import uk.ac.vamsas.objects.core.AlignmentSequence; +import uk.ac.vamsas.objects.core.AlignmentSequenceAnnotation; +import uk.ac.vamsas.objects.core.AnnotationElement; +import uk.ac.vamsas.objects.core.DataSet; +import uk.ac.vamsas.objects.core.DataSetAnnotations; +import uk.ac.vamsas.objects.core.DbRef; +import uk.ac.vamsas.objects.core.Entry; +import uk.ac.vamsas.objects.core.Glyph; +import uk.ac.vamsas.objects.core.Local; +import uk.ac.vamsas.objects.core.MapType; +import uk.ac.vamsas.objects.core.Mapped; +import uk.ac.vamsas.objects.core.Property; +import uk.ac.vamsas.objects.core.Provenance; +import uk.ac.vamsas.objects.core.RangeAnnotation; +import uk.ac.vamsas.objects.core.RangeType; +import uk.ac.vamsas.objects.core.Seg; +import uk.ac.vamsas.objects.core.Sequence; +import uk.ac.vamsas.objects.core.SequenceType; +import uk.ac.vamsas.objects.core.VAMSAS; import uk.ac.vamsas.objects.utils.Properties; /* @@ -127,7 +151,7 @@ public class VamsasAppDatastore private void buildSkipList() { skipList = new Hashtable(); - AlignFrame[] al = Desktop.getAlignframes(); + AlignFrame[] al = Desktop.getAlignFrames(); for (int f = 0; al != null && f < al.length; f++) { skipList.put(al[f].getViewport().getSequenceSetId(), al[f]); @@ -728,12 +752,12 @@ public class VamsasAppDatastore * @return true if alignment associated with this view will be stored in * document. */ - public boolean alignmentWillBeSkipped(AlignViewport av) + public boolean alignmentWillBeSkipped(AlignmentViewport av) { return (!av.getAlignment().isAligned()); } - private void addToSkipList(AlignViewport av) + private void addToSkipList(AlignmentViewport av) { if (skipList == null) { @@ -1068,8 +1092,10 @@ public class VamsasAppDatastore an.addProperty(Properties.newProperty(THRESHOLD, Properties.FLOATTYPE, "" + alan.getThreshold().value)); if (alan.getThreshold().label != null) + { an.addProperty(Properties.newProperty(THRESHOLD + "Name", Properties.STRINGTYPE, "" + alan.getThreshold().label)); + } } ((DataSet) sref.getV_parent()).addDataSetAnnotations(an); bindjvvobj(alan, an); @@ -1381,12 +1407,12 @@ public class VamsasAppDatastore // sync, // and if any contain more than one view, then remove the one generated by // document update. - AlignViewport views[], av = null; + AlignmentViewport views[], av = null; AlignFrame af = null; Iterator newviews = newAlignmentViews.iterator(); while (newviews.hasNext()) { - av = (AlignViewport) newviews.next(); + av = (AlignmentViewport) newviews.next(); af = Desktop.getAlignFrameFor(av); // TODO implement this : af.getNumberOfViews String seqsetidobj = av.getSequenceSetId(); @@ -1403,7 +1429,8 @@ public class VamsasAppDatastore // to the align frames. boolean gathered = false; String newviewid = null; - AlignedCodonFrame[] mappings = av.getAlignment().getCodonFrames(); + Set mappings = av.getAlignment() + .getCodonFrames(); for (int i = 0; i < views.length; i++) { if (views[i] != av) @@ -1438,7 +1465,7 @@ public class VamsasAppDatastore { // ensure sequence mappings from vamsas document view still // active - if (mappings != null && mappings.length > 0) + if (mappings != null) { jalview.structure.StructureSelectionManager .getStructureSelectionManager(Desktop.instance) @@ -1682,7 +1709,7 @@ public class VamsasAppDatastore uk.ac.vamsas.objects.core.Alignment alignment = dataset .getAlignment(al); // TODO check this handles multiple views properly - AlignViewport av = findViewport(alignment); + AlignmentViewport av = findViewport(alignment); jalview.datamodel.AlignmentI jal = null; if (av != null) @@ -1956,10 +1983,10 @@ public class VamsasAppDatastore return newAlignmentViews.size(); } - public AlignViewport findViewport(Alignment alignment) + public AlignmentViewport findViewport(Alignment alignment) { - AlignViewport av = null; - AlignViewport[] avs = Desktop + AlignmentViewport av = null; + AlignmentViewport[] avs = Desktop .getViewports((String) getvObj2jv(alignment)); if (avs != null) { @@ -2207,6 +2234,7 @@ public class VamsasAppDatastore Cache.log.warn("Failed to parse threshold property"); } if (val != null) + { if (gl == null) { gl = new GraphLine(val.floatValue(), "", java.awt.Color.black); @@ -2215,11 +2243,14 @@ public class VamsasAppDatastore { gl.value = val.floatValue(); } + } } else if (props[p].getName().equalsIgnoreCase(THRESHOLD + "Name")) { if (gl == null) + { gl = new GraphLine(0, "", java.awt.Color.black); + } gl.label = props[p].getContent(); } } @@ -2670,10 +2701,10 @@ public class VamsasAppDatastore return vobj2jv; } - public void storeSequenceMappings(AlignViewport viewport, String title) + public void storeSequenceMappings(AlignmentViewport viewport, String title) throws Exception { - AlignViewport av = viewport; + AlignmentViewport av = viewport; try { jalview.datamodel.AlignmentI jal = av.getAlignment(); @@ -2695,18 +2726,15 @@ public class VamsasAppDatastore } // Store any sequence mappings. - if (av.getAlignment().getCodonFrames() != null - && av.getAlignment().getCodonFrames().length > 0) + Set cframes = av.getAlignment().getCodonFrames(); + if (cframes != null) { - jalview.datamodel.AlignedCodonFrame[] cframes = av.getAlignment() - .getCodonFrames(); - for (int cf = 0; cf < cframes.length; cf++) + for (AlignedCodonFrame acf : cframes) { - if (cframes[cf].getdnaSeqs() != null - && cframes[cf].getdnaSeqs().length > 0) + if (acf.getdnaSeqs() != null && acf.getdnaSeqs().length > 0) { - jalview.datamodel.SequenceI[] dmps = cframes[cf].getdnaSeqs(); - jalview.datamodel.Mapping[] mps = cframes[cf].getProtMappings(); + jalview.datamodel.SequenceI[] dmps = acf.getdnaSeqs(); + jalview.datamodel.Mapping[] mps = acf.getProtMappings(); for (int smp = 0; smp < mps.length; smp++) { uk.ac.vamsas.objects.core.SequenceType mfrom = (SequenceType) getjv2vObj(dmps[smp]); diff --git a/src/jalview/io/vamsas/Sequencemapping.java b/src/jalview/io/vamsas/Sequencemapping.java index 3878a0a..3e20dcd 100644 --- a/src/jalview/io/vamsas/Sequencemapping.java +++ b/src/jalview/io/vamsas/Sequencemapping.java @@ -20,14 +20,15 @@ */ package jalview.io.vamsas; -import java.util.Vector; - import jalview.datamodel.AlignedCodonFrame; +import jalview.datamodel.AlignmentI; import jalview.datamodel.Mapping; import jalview.datamodel.SequenceI; import jalview.gui.Desktop; import jalview.io.VamsasAppDatastore; -import uk.ac.vamsas.client.Vobject; + +import java.util.Vector; + import uk.ac.vamsas.objects.core.AlignmentSequence; import uk.ac.vamsas.objects.core.DataSet; import uk.ac.vamsas.objects.core.Sequence; @@ -283,12 +284,12 @@ public class Sequencemapping extends Rangetype jalview.bin.Cache.log.info("Ignoring non sequence-sequence mapping"); return; } - mobj = this.getvObj2jv((Vobject) sdloc); + mobj = this.getvObj2jv(sdloc); if (mobj instanceof SequenceI) { from = (SequenceI) mobj; } - mobj = this.getvObj2jv((Vobject) sdmap); + mobj = this.getvObj2jv(sdmap); if (mobj instanceof SequenceI) { to = (SequenceI) mobj; @@ -325,19 +326,17 @@ public class Sequencemapping extends Rangetype } // create mapping storage object and make each dataset alignment reference // it. - jalview.datamodel.AlignmentI dsLoc = (jalview.datamodel.AlignmentI) getvObj2jv(sdloc - .getV_parent()); - jalview.datamodel.AlignmentI dsMap = (jalview.datamodel.AlignmentI) getvObj2jv(sdmap - .getV_parent()); - AlignedCodonFrame afc = new AlignedCodonFrame(0); + AlignmentI dsLoc = (AlignmentI) getvObj2jv(sdloc.getV_parent()); + AlignmentI dsMap = (AlignmentI) getvObj2jv(sdmap.getV_parent()); + AlignedCodonFrame acf = new AlignedCodonFrame(); if (dsLoc != null && dsLoc != dsMap) { - dsLoc.addCodonFrame(afc); + dsLoc.addCodonFrame(acf); } if (dsMap != null) { - dsMap.addCodonFrame(afc); + dsMap.addCodonFrame(acf); } // create and add the new mapping to (each) dataset's codonFrame @@ -350,24 +349,22 @@ public class Sequencemapping extends Rangetype mapping = new jalview.util.MapList(mapping.getToRanges(), mapping.getFromRanges(), mapping.getToRatio(), mapping.getFromRatio()); - afc.addMap(to, from, mapping); + acf.addMap(to, from, mapping); } else { mapping = this.parsemapType(sequenceMapping, 3, 1); // correct sense - afc.addMap(from, to, mapping); + acf.addMap(from, to, mapping); } } else { mapping = this.parsemapType(sequenceMapping, 1, 1); // correct sense - afc.addMap(from, to, mapping); + acf.addMap(from, to, mapping); } bindjvvobj(mapping, sequenceMapping); jalview.structure.StructureSelectionManager - .getStructureSelectionManager(Desktop.instance).addMappings( - new AlignedCodonFrame[] - { afc }); + .getStructureSelectionManager(Desktop.instance).addMapping(acf); // Try to link up any conjugate database references in the two sequences // matchConjugateDBRefs(from, to, mapping); // Try to propagate any dbrefs across this mapping. diff --git a/src/jalview/io/vamsas/Tree.java b/src/jalview/io/vamsas/Tree.java index dbfaf37..40d9f7c 100644 --- a/src/jalview/io/vamsas/Tree.java +++ b/src/jalview/io/vamsas/Tree.java @@ -35,10 +35,10 @@ import jalview.datamodel.SeqCigar; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceI; import jalview.datamodel.SequenceNode; -import jalview.gui.AlignViewport; import jalview.gui.TreePanel; import jalview.io.NewickFile; import jalview.io.VamsasAppDatastore; +import jalview.viewmodel.AlignmentViewport; import uk.ac.vamsas.client.Vobject; import uk.ac.vamsas.objects.core.AlignmentSequence; import uk.ac.vamsas.objects.core.Entry; @@ -510,7 +510,7 @@ public class Tree extends DatastoreItem */ public Object[] recoverInputData(Provenance tp) { - AlignViewport javport = null; + AlignmentViewport javport = null; jalview.datamodel.AlignmentI jal = null; jalview.datamodel.CigarArray view = null; for (int pe = 0; pe < tp.getEntryCount(); pe++) @@ -604,7 +604,7 @@ public class Tree extends DatastoreItem return null; } - private AlignViewport getViewport(Vobject v_parent) + private AlignmentViewport getViewport(Vobject v_parent) { if (v_parent instanceof uk.ac.vamsas.objects.core.Alignment) { diff --git a/src/jalview/javascript/MouseOverListener.java b/src/jalview/javascript/MouseOverListener.java index 19f7b11..2b0aab2 100644 --- a/src/jalview/javascript/MouseOverListener.java +++ b/src/jalview/javascript/MouseOverListener.java @@ -37,7 +37,9 @@ public class MouseOverListener extends JSFunctionExec implements 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) { diff --git a/src/jalview/javascript/MouseOverStructureListener.java b/src/jalview/javascript/MouseOverStructureListener.java index 2ecaf6c..3cc7c98 100644 --- a/src/jalview/javascript/MouseOverStructureListener.java +++ b/src/jalview/javascript/MouseOverStructureListener.java @@ -20,9 +20,6 @@ */ package jalview.javascript; -import java.awt.Color; -import java.util.ArrayList; - import jalview.api.AlignmentViewPanel; import jalview.api.FeatureRenderer; import jalview.api.SequenceRenderer; @@ -30,11 +27,15 @@ import jalview.appletgui.AlignFrame; 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 @@ -133,7 +134,6 @@ public class MouseOverStructureListener extends JSFunctionExec implements return modelSet; } - @Override public void mouseOverStructure(int atomIndex, String strInfo) { @@ -144,24 +144,25 @@ public class MouseOverStructureListener extends JSFunctionExec implements } @Override - public void highlightAtom(int atomIndex, int pdbResNum, String chain, - String pdbId) + public void highlightAtoms(List 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 + { + // TODO is this right? StructureSelectionManager passes pdbFile as the + // field that is interpreted (in 2.8.2) as pdbId? + executeJavascriptFunction(_listenerfn, new String[] + { "mouseover", "" + atom.getPdbFile(), + "" + atom.getChain(), + "" + (atom.getPdbResNum()), "" + atom.getAtomIndex() }); + } catch (Exception ex) + { + System.err.println("Couldn't execute callback with " + _listenerfn + + " for atomSpec: " + atom); + ex.printStackTrace(); + } } - } @Override @@ -283,13 +284,6 @@ public class MouseOverStructureListener extends JSFunctionExec implements } @Override - public Color getColour(int atomIndex, int pdbResNum, String chain, - String pdbId) - { - return null; - } - - @Override public AlignFrame getAlignFrame() { // associated with all alignframes, always. diff --git a/src/jalview/jbgui/GAlignFrame.java b/src/jalview/jbgui/GAlignFrame.java index 4ecedaf..1cfc73c 100755 --- a/src/jalview/jbgui/GAlignFrame.java +++ b/src/jalview/jbgui/GAlignFrame.java @@ -35,8 +35,11 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; +import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.util.HashMap; +import java.util.Map; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; @@ -50,6 +53,7 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JRadioButtonMenuItem; import javax.swing.JTabbedPane; +import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.MenuEvent; @@ -256,6 +260,8 @@ public class GAlignFrame extends JInternalFrame protected JMenuItem showTranslation = new JMenuItem(); + protected JMenu cdna = new JMenu(); + protected JMenuItem extractScores = new JMenuItem(); protected JMenuItem expandAlignment = new JMenuItem(); @@ -388,6 +394,8 @@ public class GAlignFrame extends JInternalFrame private boolean showAutoCalculatedAbove = false; + private Map accelerators = new HashMap(); + public GAlignFrame() { try @@ -401,7 +409,7 @@ public class GAlignFrame extends JInternalFrame JMenuItem item = new JMenuItem( jalview.io.FormatAdapter.WRITEABLE_FORMATS[i]); - item.addActionListener(new java.awt.event.ActionListener() + item.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -610,81 +618,85 @@ public class GAlignFrame extends JInternalFrame private void jbInit() throws Exception { fileMenu.setText(MessageManager.getString("action.file")); + saveAs.setText(MessageManager.getString("action.save_as") + "..."); - saveAs.setAccelerator(javax.swing.KeyStroke.getKeyStroke( - java.awt.event.KeyEvent.VK_S, Toolkit.getDefaultToolkit() - .getMenuShortcutKeyMask() - | java.awt.event.KeyEvent.SHIFT_MASK, false)); - saveAs.addActionListener(new ActionListener() + ActionListener al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { saveAs_actionPerformed(e); } - }); + }; + KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_S, Toolkit + .getDefaultToolkit().getMenuShortcutKeyMask() + | KeyEvent.SHIFT_MASK, false); + addMenuActionAndAccelerator(keyStroke, saveAs, al); + closeMenuItem.setText(MessageManager.getString("action.close")); - closeMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke( - java.awt.event.KeyEvent.VK_W, Toolkit.getDefaultToolkit() - .getMenuShortcutKeyMask(), false)); - closeMenuItem.addActionListener(new java.awt.event.ActionListener() + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_W, Toolkit + .getDefaultToolkit().getMenuShortcutKeyMask(), false); + al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { closeMenuItem_actionPerformed(false); } - }); + }; + addMenuActionAndAccelerator(keyStroke, closeMenuItem, al); + editMenu.setText(MessageManager.getString("action.edit")); viewMenu.setText(MessageManager.getString("action.view")); annotationsMenu.setText(MessageManager.getString("action.annotations")); colourMenu.setText(MessageManager.getString("action.colour")); calculateMenu.setText(MessageManager.getString("action.calculate")); webService.setText(MessageManager.getString("action.web_service")); + selectAllSequenceMenuItem.setText(MessageManager .getString("action.select_all")); - selectAllSequenceMenuItem.setAccelerator(javax.swing.KeyStroke - .getKeyStroke(java.awt.event.KeyEvent.VK_A, Toolkit - .getDefaultToolkit().getMenuShortcutKeyMask(), false)); - selectAllSequenceMenuItem - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - selectAllSequenceMenuItem_actionPerformed(e); - } - }); + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_A, Toolkit + .getDefaultToolkit().getMenuShortcutKeyMask(), false); + al = new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + selectAllSequenceMenuItem_actionPerformed(e); + } + }; + addMenuActionAndAccelerator(keyStroke, selectAllSequenceMenuItem, al); + deselectAllSequenceMenuItem.setText(MessageManager .getString("action.deselect_all")); - deselectAllSequenceMenuItem.setAccelerator(javax.swing.KeyStroke - .getKeyStroke(java.awt.event.KeyEvent.VK_ESCAPE, 0, false)); - deselectAllSequenceMenuItem - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - deselectAllSequenceMenuItem_actionPerformed(e); - } - }); + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false); + al = new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + deselectAllSequenceMenuItem_actionPerformed(e); + } + }; + addMenuActionAndAccelerator(keyStroke, deselectAllSequenceMenuItem, al); + invertSequenceMenuItem.setText(MessageManager .getString("action.invert_sequence_selection")); - invertSequenceMenuItem.setAccelerator(javax.swing.KeyStroke - .getKeyStroke(java.awt.event.KeyEvent.VK_I, Toolkit - .getDefaultToolkit().getMenuShortcutKeyMask(), false)); - invertSequenceMenuItem - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - invertSequenceMenuItem_actionPerformed(e); - } - }); + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_I, Toolkit + .getDefaultToolkit().getMenuShortcutKeyMask(), false); + al = new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + invertSequenceMenuItem_actionPerformed(e); + } + }; + addMenuActionAndAccelerator(keyStroke, invertSequenceMenuItem, al); + grpsFromSelection.setText(MessageManager .getString("action.make_groups_selection")); - grpsFromSelection.addActionListener(new java.awt.event.ActionListener() + grpsFromSelection.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -696,7 +708,7 @@ public class GAlignFrame extends JInternalFrame .getString("action.view_flanking_regions")); expandAlignment.setToolTipText(MessageManager .getString("label.view_flanking_regions")); - expandAlignment.addActionListener(new java.awt.event.ActionListener() + expandAlignment.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -706,86 +718,84 @@ public class GAlignFrame extends JInternalFrame }); remove2LeftMenuItem.setText(MessageManager .getString("action.remove_left")); - remove2LeftMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke( - java.awt.event.KeyEvent.VK_L, Toolkit.getDefaultToolkit() - .getMenuShortcutKeyMask(), false)); - remove2LeftMenuItem - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - remove2LeftMenuItem_actionPerformed(e); - } - }); + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_L, Toolkit + .getDefaultToolkit().getMenuShortcutKeyMask(), false); + al = new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + remove2LeftMenuItem_actionPerformed(e); + } + }; + addMenuActionAndAccelerator(keyStroke, remove2LeftMenuItem, al); + remove2RightMenuItem.setText(MessageManager .getString("action.remove_right")); - remove2RightMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke( - java.awt.event.KeyEvent.VK_R, Toolkit.getDefaultToolkit() - .getMenuShortcutKeyMask(), false)); - remove2RightMenuItem - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - remove2RightMenuItem_actionPerformed(e); - } - }); + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_R, Toolkit + .getDefaultToolkit().getMenuShortcutKeyMask(), false); + al = new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + remove2RightMenuItem_actionPerformed(e); + } + }; + addMenuActionAndAccelerator(keyStroke, remove2RightMenuItem, al); + removeGappedColumnMenuItem.setText(MessageManager .getString("action.remove_empty_columns")); - removeGappedColumnMenuItem.setAccelerator(javax.swing.KeyStroke - .getKeyStroke(java.awt.event.KeyEvent.VK_E, Toolkit - .getDefaultToolkit().getMenuShortcutKeyMask(), false)); - removeGappedColumnMenuItem - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - removeGappedColumnMenuItem_actionPerformed(e); - } - }); + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_E, Toolkit + .getDefaultToolkit().getMenuShortcutKeyMask(), false); + al = new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + removeGappedColumnMenuItem_actionPerformed(e); + } + }; + addMenuActionAndAccelerator(keyStroke, removeGappedColumnMenuItem, al); + removeAllGapsMenuItem.setText(MessageManager .getString("action.remove_all_gaps")); - removeAllGapsMenuItem.setAccelerator(javax.swing.KeyStroke - .getKeyStroke(java.awt.event.KeyEvent.VK_E, Toolkit - .getDefaultToolkit().getMenuShortcutKeyMask() - | java.awt.event.KeyEvent.SHIFT_MASK, false)); - removeAllGapsMenuItem - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - removeAllGapsMenuItem_actionPerformed(e); - } - }); + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_E, Toolkit + .getDefaultToolkit().getMenuShortcutKeyMask() + | KeyEvent.SHIFT_MASK, false); + al = new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + removeAllGapsMenuItem_actionPerformed(e); + } + }; + addMenuActionAndAccelerator(keyStroke, removeAllGapsMenuItem, al); + justifyLeftMenuItem.setText(MessageManager .getString("action.left_justify_alignment")); - justifyLeftMenuItem - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - justifyLeftMenuItem_actionPerformed(e); - } - }); + justifyLeftMenuItem.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + justifyLeftMenuItem_actionPerformed(e); + } + }); justifyRightMenuItem.setText(MessageManager .getString("action.right_justify_alignment")); - justifyRightMenuItem - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - justifyRightMenuItem_actionPerformed(e); - } - }); + justifyRightMenuItem.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + justifyRightMenuItem_actionPerformed(e); + } + }); viewBoxesMenuItem.setText(MessageManager.getString("action.boxes")); viewBoxesMenuItem.setState(true); - viewBoxesMenuItem.addActionListener(new java.awt.event.ActionListener() + viewBoxesMenuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -795,7 +805,7 @@ public class GAlignFrame extends JInternalFrame }); viewTextMenuItem.setText(MessageManager.getString("action.text")); viewTextMenuItem.setState(true); - viewTextMenuItem.addActionListener(new java.awt.event.ActionListener() + viewTextMenuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -806,28 +816,26 @@ public class GAlignFrame extends JInternalFrame showNonconservedMenuItem.setText(MessageManager .getString("label.show_non_conversed")); showNonconservedMenuItem.setState(false); - showNonconservedMenuItem - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - showUnconservedMenuItem_actionPerformed(e); - } - }); + showNonconservedMenuItem.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + showUnconservedMenuItem_actionPerformed(e); + } + }); sortPairwiseMenuItem.setText(MessageManager .getString("action.by_pairwise_id")); - sortPairwiseMenuItem - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - sortPairwiseMenuItem_actionPerformed(e); - } - }); + sortPairwiseMenuItem.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + sortPairwiseMenuItem_actionPerformed(e); + } + }); sortIDMenuItem.setText(MessageManager.getString("action.by_id")); - sortIDMenuItem.addActionListener(new java.awt.event.ActionListener() + sortIDMenuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -837,17 +845,16 @@ public class GAlignFrame extends JInternalFrame }); sortLengthMenuItem .setText(MessageManager.getString("action.by_length")); - sortLengthMenuItem - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - sortLengthMenuItem_actionPerformed(e); - } - }); + sortLengthMenuItem.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + sortLengthMenuItem_actionPerformed(e); + } + }); sortGroupMenuItem.setText(MessageManager.getString("action.by_group")); - sortGroupMenuItem.addActionListener(new java.awt.event.ActionListener() + sortGroupMenuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -855,34 +862,34 @@ public class GAlignFrame extends JInternalFrame sortGroupMenuItem_actionPerformed(e); } }); - removeRedundancyMenuItem.setText(MessageManager - .getString("action.remove_redundancy").concat("...")); - removeRedundancyMenuItem.setAccelerator(javax.swing.KeyStroke - .getKeyStroke(java.awt.event.KeyEvent.VK_D, Toolkit - .getDefaultToolkit().getMenuShortcutKeyMask(), false)); - removeRedundancyMenuItem - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - removeRedundancyMenuItem_actionPerformed(e); - } - }); + + removeRedundancyMenuItem.setText(MessageManager.getString( + "action.remove_redundancy").concat("...")); + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_D, Toolkit + .getDefaultToolkit().getMenuShortcutKeyMask(), false); + al = new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + removeRedundancyMenuItem_actionPerformed(e); + } + }; + addMenuActionAndAccelerator(keyStroke, removeRedundancyMenuItem, al); + pairwiseAlignmentMenuItem.setText(MessageManager .getString("action.pairwise_alignment")); - pairwiseAlignmentMenuItem - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - pairwiseAlignmentMenuItem_actionPerformed(e); - } - }); + pairwiseAlignmentMenuItem.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + pairwiseAlignmentMenuItem_actionPerformed(e); + } + }); PCAMenuItem.setText(MessageManager .getString("label.principal_component_analysis")); - PCAMenuItem.addActionListener(new java.awt.event.ActionListener() + PCAMenuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -892,26 +899,24 @@ public class GAlignFrame extends JInternalFrame }); averageDistanceTreeMenuItem.setText(MessageManager .getString("label.average_distance_identity")); - averageDistanceTreeMenuItem - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - averageDistanceTreeMenuItem_actionPerformed(e); - } - }); + averageDistanceTreeMenuItem.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + averageDistanceTreeMenuItem_actionPerformed(e); + } + }); neighbourTreeMenuItem.setText(MessageManager .getString("label.neighbour_joining_identity")); - neighbourTreeMenuItem - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - neighbourTreeMenuItem_actionPerformed(e); - } - }); + neighbourTreeMenuItem.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + neighbourTreeMenuItem_actionPerformed(e); + } + }); this.getContentPane().setLayout(borderLayout1); alignFrameMenuBar.setFont(new java.awt.Font("Verdana", 0, 11)); statusBar.setBackground(Color.white); @@ -922,7 +927,7 @@ public class GAlignFrame extends JInternalFrame .getString("label.out_to_textbox")); clustalColour.setText(MessageManager.getString("label.clustalx")); - clustalColour.addActionListener(new java.awt.event.ActionListener() + clustalColour.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -931,7 +936,7 @@ public class GAlignFrame extends JInternalFrame } }); zappoColour.setText(MessageManager.getString("label.zappo")); - zappoColour.addActionListener(new java.awt.event.ActionListener() + zappoColour.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -940,7 +945,7 @@ public class GAlignFrame extends JInternalFrame } }); taylorColour.setText(MessageManager.getString("label.taylor")); - taylorColour.addActionListener(new java.awt.event.ActionListener() + taylorColour.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -950,17 +955,16 @@ public class GAlignFrame extends JInternalFrame }); hydrophobicityColour.setText(MessageManager .getString("label.hydrophobicity")); - hydrophobicityColour - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - hydrophobicityColour_actionPerformed(e); - } - }); + hydrophobicityColour.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + hydrophobicityColour_actionPerformed(e); + } + }); helixColour.setText(MessageManager.getString("label.helix_propensity")); - helixColour.addActionListener(new java.awt.event.ActionListener() + helixColour.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -970,7 +974,7 @@ public class GAlignFrame extends JInternalFrame }); strandColour.setText(MessageManager .getString("label.strand_propensity")); - strandColour.addActionListener(new java.awt.event.ActionListener() + strandColour.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -979,7 +983,7 @@ public class GAlignFrame extends JInternalFrame } }); turnColour.setText(MessageManager.getString("label.turn_propensity")); - turnColour.addActionListener(new java.awt.event.ActionListener() + turnColour.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -988,7 +992,7 @@ public class GAlignFrame extends JInternalFrame } }); buriedColour.setText(MessageManager.getString("label.buried_index")); - buriedColour.addActionListener(new java.awt.event.ActionListener() + buriedColour.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -998,7 +1002,7 @@ public class GAlignFrame extends JInternalFrame }); userDefinedColour.setText(MessageManager .getString("action.user_defined")); - userDefinedColour.addActionListener(new java.awt.event.ActionListener() + userDefinedColour.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -1008,7 +1012,7 @@ public class GAlignFrame extends JInternalFrame }); PIDColour .setText(MessageManager.getString("label.percentage_identity")); - PIDColour.addActionListener(new java.awt.event.ActionListener() + PIDColour.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -1018,7 +1022,7 @@ public class GAlignFrame extends JInternalFrame }); BLOSUM62Colour .setText(MessageManager.getString("label.blosum62_score")); - BLOSUM62Colour.addActionListener(new java.awt.event.ActionListener() + BLOSUM62Colour.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -1027,7 +1031,7 @@ public class GAlignFrame extends JInternalFrame } }); nucleotideColour.setText(MessageManager.getString("label.nucleotide")); - nucleotideColour.addActionListener(new java.awt.event.ActionListener() + nucleotideColour.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -1038,55 +1042,51 @@ public class GAlignFrame extends JInternalFrame purinePyrimidineColour.setText(MessageManager .getString("label.purine_pyrimidine")); - purinePyrimidineColour - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - purinePyrimidineColour_actionPerformed(e); - } - }); + purinePyrimidineColour.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + purinePyrimidineColour_actionPerformed(e); + } + }); RNAInteractionColour.setText("RNA Interaction type"); - RNAInteractionColour - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - RNAInteractionColour_actionPerformed(e); - } - }); + RNAInteractionColour.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + RNAInteractionColour_actionPerformed(e); + } + }); /* * covariationColour.setText("Covariation"); - * covariationColour.addActionListener(new java.awt.event.ActionListener() { - * public void actionPerformed(ActionEvent e) { - * covariationColour_actionPerformed(e); } }); + * covariationColour.addActionListener(new ActionListener() { public void + * actionPerformed(ActionEvent e) { covariationColour_actionPerformed(e); } + * }); */ avDistanceTreeBlosumMenuItem.setText(MessageManager .getString("label.average_distance_bloslum62")); - avDistanceTreeBlosumMenuItem - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - avTreeBlosumMenuItem_actionPerformed(e); - } - }); + avDistanceTreeBlosumMenuItem.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + avTreeBlosumMenuItem_actionPerformed(e); + } + }); njTreeBlosumMenuItem.setText(MessageManager .getString("label.neighbour_blosum62")); - njTreeBlosumMenuItem - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - njTreeBlosumMenuItem_actionPerformed(e); - } - }); + njTreeBlosumMenuItem.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + njTreeBlosumMenuItem_actionPerformed(e); + } + }); annotationPanelMenuItem.setActionCommand(""); annotationPanelMenuItem.setText(MessageManager .getString("label.show_annotations")); @@ -1183,17 +1183,16 @@ public class GAlignFrame extends JInternalFrame }); colourTextMenuItem.setText(MessageManager .getString("label.colour_text")); - colourTextMenuItem - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - colourTextMenuItem_actionPerformed(e); - } - }); + colourTextMenuItem.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + colourTextMenuItem_actionPerformed(e); + } + }); htmlMenuItem.setText(MessageManager.getString("label.html")); - htmlMenuItem.addActionListener(new java.awt.event.ActionListener() + htmlMenuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -1216,7 +1215,7 @@ public class GAlignFrame extends JInternalFrame overviewMenuItem.setText(MessageManager .getString("label.overview_window")); - overviewMenuItem.addActionListener(new java.awt.event.ActionListener() + overviewMenuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -1224,45 +1223,47 @@ public class GAlignFrame extends JInternalFrame overviewMenuItem_actionPerformed(e); } }); + undoMenuItem.setEnabled(false); undoMenuItem.setText(MessageManager.getString("action.undo")); - undoMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke( - java.awt.event.KeyEvent.VK_Z, Toolkit.getDefaultToolkit() - .getMenuShortcutKeyMask(), false)); - undoMenuItem.addActionListener(new java.awt.event.ActionListener() + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_Z, Toolkit + .getDefaultToolkit().getMenuShortcutKeyMask(), false); + al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { undoMenuItem_actionPerformed(e); } - }); + }; + addMenuActionAndAccelerator(keyStroke, undoMenuItem, al); + redoMenuItem.setEnabled(false); redoMenuItem.setText(MessageManager.getString("action.redo")); - redoMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke( - java.awt.event.KeyEvent.VK_Y, Toolkit.getDefaultToolkit() - .getMenuShortcutKeyMask(), false)); - redoMenuItem.addActionListener(new java.awt.event.ActionListener() + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_Y, Toolkit + .getDefaultToolkit().getMenuShortcutKeyMask(), false); + al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { redoMenuItem_actionPerformed(e); } - }); + }; + addMenuActionAndAccelerator(keyStroke, redoMenuItem, al); + conservationMenuItem.setText(MessageManager .getString("action.by_conservation")); - conservationMenuItem - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - conservationMenuItem_actionPerformed(e); - } - }); + conservationMenuItem.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + conservationMenuItem_actionPerformed(e); + } + }); noColourmenuItem.setText(MessageManager.getString("label.none")); - noColourmenuItem.addActionListener(new java.awt.event.ActionListener() + noColourmenuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -1271,7 +1272,7 @@ public class GAlignFrame extends JInternalFrame } }); wrapMenuItem.setText(MessageManager.getString("label.wrap")); - wrapMenuItem.addActionListener(new java.awt.event.ActionListener() + wrapMenuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -1279,47 +1280,50 @@ public class GAlignFrame extends JInternalFrame wrapMenuItem_actionPerformed(e); } }); + printMenuItem.setText(MessageManager.getString("action.print") + "..."); - printMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke( - java.awt.event.KeyEvent.VK_P, Toolkit.getDefaultToolkit() - .getMenuShortcutKeyMask(), false)); - printMenuItem.addActionListener(new java.awt.event.ActionListener() + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_P, Toolkit + .getDefaultToolkit().getMenuShortcutKeyMask(), false); + al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { printMenuItem_actionPerformed(e); } - }); + }; + addMenuActionAndAccelerator(keyStroke, printMenuItem, al); + renderGapsMenuItem .setText(MessageManager.getString("action.show_gaps")); renderGapsMenuItem.setState(true); - renderGapsMenuItem - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - renderGapsMenuItem_actionPerformed(e); - } - }); + renderGapsMenuItem.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + renderGapsMenuItem_actionPerformed(e); + } + }); + findMenuItem.setText(MessageManager.getString("action.find")); - findMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke( - java.awt.event.KeyEvent.VK_F, Toolkit.getDefaultToolkit() - .getMenuShortcutKeyMask(), false)); + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F, Toolkit + .getDefaultToolkit().getMenuShortcutKeyMask(), false); findMenuItem.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.getString("label.find_tip"))); - findMenuItem.addActionListener(new java.awt.event.ActionListener() + al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { findMenuItem_actionPerformed(e); } - }); + }; + addMenuActionAndAccelerator(keyStroke, findMenuItem, al); + abovePIDThreshold.setText(MessageManager .getString("label.above_identity_threshold")); - abovePIDThreshold.addActionListener(new java.awt.event.ActionListener() + abovePIDThreshold.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -1446,8 +1450,7 @@ public class GAlignFrame extends JInternalFrame buttonGroup.add(showAutoLast); showAutoFirst.setText(MessageManager.getString("label.show_first")); showAutoFirst.setSelected(Cache.getDefault( - Preferences.SHOW_AUTOCALC_ABOVE, - false)); + Preferences.SHOW_AUTOCALC_ABOVE, false)); showAutoFirst.addActionListener(new ActionListener() { @Override @@ -1470,7 +1473,7 @@ public class GAlignFrame extends JInternalFrame }); nucleotideColour.setText(MessageManager.getString("label.nucleotide")); - nucleotideColour.addActionListener(new java.awt.event.ActionListener() + nucleotideColour.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -1493,69 +1496,74 @@ public class GAlignFrame extends JInternalFrame deleteGroups .setText(MessageManager.getString("action.undefine_groups")); - deleteGroups.setAccelerator(javax.swing.KeyStroke.getKeyStroke( - java.awt.event.KeyEvent.VK_U, Toolkit.getDefaultToolkit() - .getMenuShortcutKeyMask(), false)); - deleteGroups.addActionListener(new java.awt.event.ActionListener() + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_U, Toolkit + .getDefaultToolkit().getMenuShortcutKeyMask(), false); + al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { deleteGroups_actionPerformed(e); } - }); + }; + addMenuActionAndAccelerator(keyStroke, deleteGroups, al); + createGroup.setText(MessageManager.getString("action.create_groups")); - createGroup.setAccelerator(javax.swing.KeyStroke.getKeyStroke( - java.awt.event.KeyEvent.VK_G, Toolkit.getDefaultToolkit() - .getMenuShortcutKeyMask(), false)); - createGroup.addActionListener(new java.awt.event.ActionListener() + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_G, Toolkit + .getDefaultToolkit().getMenuShortcutKeyMask(), false); + al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { createGroup_actionPerformed(e); } - }); + }; + addMenuActionAndAccelerator(keyStroke, createGroup, al); + unGroup.setText(MessageManager.getString("action.remove_group")); - unGroup.setAccelerator(javax.swing.KeyStroke.getKeyStroke( - java.awt.event.KeyEvent.VK_G, Toolkit.getDefaultToolkit() - .getMenuShortcutKeyMask() - | java.awt.event.KeyEvent.SHIFT_MASK, false)); - unGroup.addActionListener(new java.awt.event.ActionListener() + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_G, Toolkit + .getDefaultToolkit().getMenuShortcutKeyMask() + | KeyEvent.SHIFT_MASK, false); + al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { unGroup_actionPerformed(e); } - }); + }; + addMenuActionAndAccelerator(keyStroke, unGroup, al); + copy.setText(MessageManager.getString("action.copy")); - copy.setAccelerator(javax.swing.KeyStroke.getKeyStroke( - java.awt.event.KeyEvent.VK_C, Toolkit.getDefaultToolkit() - .getMenuShortcutKeyMask(), false)); + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit + .getDefaultToolkit().getMenuShortcutKeyMask(), false); - copy.addActionListener(new java.awt.event.ActionListener() + al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { copy_actionPerformed(e); } - }); + }; + addMenuActionAndAccelerator(keyStroke, copy, al); + cut.setText(MessageManager.getString("action.cut")); - cut.setAccelerator(javax.swing.KeyStroke.getKeyStroke( - java.awt.event.KeyEvent.VK_X, Toolkit.getDefaultToolkit() - .getMenuShortcutKeyMask(), false)); - cut.addActionListener(new java.awt.event.ActionListener() + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_X, Toolkit + .getDefaultToolkit().getMenuShortcutKeyMask(), false); + al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { cut_actionPerformed(e); } - }); + }; + addMenuActionAndAccelerator(keyStroke, cut, al); + delete.setText(MessageManager.getString("action.delete")); - delete.addActionListener(new java.awt.event.ActionListener() + delete.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -1563,35 +1571,38 @@ public class GAlignFrame extends JInternalFrame delete_actionPerformed(e); } }); + pasteMenu.setText(MessageManager.getString("action.paste")); pasteNew.setText(MessageManager.getString("label.to_new_alignment")); - pasteNew.setAccelerator(javax.swing.KeyStroke.getKeyStroke( - java.awt.event.KeyEvent.VK_V, Toolkit.getDefaultToolkit() - .getMenuShortcutKeyMask() - | java.awt.event.KeyEvent.SHIFT_MASK, false)); - pasteNew.addActionListener(new java.awt.event.ActionListener() + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit + .getDefaultToolkit().getMenuShortcutKeyMask() + | KeyEvent.SHIFT_MASK, false); + al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { pasteNew_actionPerformed(e); } - }); + }; + addMenuActionAndAccelerator(keyStroke, pasteNew, al); + pasteThis.setText(MessageManager.getString("label.to_this_alignment")); - pasteThis.setAccelerator(javax.swing.KeyStroke.getKeyStroke( - java.awt.event.KeyEvent.VK_V, Toolkit.getDefaultToolkit() - .getMenuShortcutKeyMask(), false)); - pasteThis.addActionListener(new java.awt.event.ActionListener() + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit + .getDefaultToolkit().getMenuShortcutKeyMask(), false); + al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { pasteThis_actionPerformed(e); } - }); + }; + addMenuActionAndAccelerator(keyStroke, pasteThis, al); + applyToAllGroups.setText(MessageManager .getString("label.apply_colour_to_all_groups")); - applyToAllGroups.addActionListener(new java.awt.event.ActionListener() + applyToAllGroups.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -1599,7 +1610,7 @@ public class GAlignFrame extends JInternalFrame applyToAllGroups_actionPerformed(e); } }); - createPNG.addActionListener(new java.awt.event.ActionListener() + createPNG.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -1612,7 +1623,7 @@ public class GAlignFrame extends JInternalFrame createPNG.setText("PNG"); font.setText(MessageManager.getString("action.font")); - font.addActionListener(new java.awt.event.ActionListener() + font.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -1623,7 +1634,7 @@ public class GAlignFrame extends JInternalFrame seqLimits.setText(MessageManager .getString("label.show_sequence_limits")); seqLimits.setState(jalview.bin.Cache.getDefault("SHOW_JVSUFFIX", true)); - seqLimits.addActionListener(new java.awt.event.ActionListener() + seqLimits.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -1632,7 +1643,7 @@ public class GAlignFrame extends JInternalFrame } }); epsFile.setText("EPS"); - epsFile.addActionListener(new java.awt.event.ActionListener() + epsFile.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -1642,7 +1653,7 @@ public class GAlignFrame extends JInternalFrame }); createSVG.setText("SVG"); - createSVG.addActionListener(new java.awt.event.ActionListener() + createSVG.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -1655,7 +1666,7 @@ public class GAlignFrame extends JInternalFrame .getString("label.load_tree_for_sequence_set")); LoadtreeMenuItem.setText(MessageManager .getString("label.load_associated_tree")); - LoadtreeMenuItem.addActionListener(new java.awt.event.ActionListener() + LoadtreeMenuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -1666,7 +1677,7 @@ public class GAlignFrame extends JInternalFrame scaleAbove.setVisible(false); scaleAbove.setText(MessageManager.getString("action.scale_above")); - scaleAbove.addActionListener(new java.awt.event.ActionListener() + scaleAbove.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -1677,7 +1688,7 @@ public class GAlignFrame extends JInternalFrame scaleLeft.setVisible(false); scaleLeft.setSelected(true); scaleLeft.setText(MessageManager.getString("action.scale_left")); - scaleLeft.addActionListener(new java.awt.event.ActionListener() + scaleLeft.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -1688,7 +1699,7 @@ public class GAlignFrame extends JInternalFrame scaleRight.setVisible(false); scaleRight.setSelected(true); scaleRight.setText(MessageManager.getString("action.scale_right")); - scaleRight.addActionListener(new java.awt.event.ActionListener() + scaleRight.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -1700,15 +1711,14 @@ public class GAlignFrame extends JInternalFrame centreColumnLabelsMenuItem.setState(false); centreColumnLabelsMenuItem.setText(MessageManager .getString("label.centre_column_labels")); - centreColumnLabelsMenuItem - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - centreColumnLabels_actionPerformed(e); - } - }); + centreColumnLabelsMenuItem.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + centreColumnLabels_actionPerformed(e); + } + }); followHighlightMenuItem.setVisible(true); followHighlightMenuItem.setState(true); followHighlightMenuItem.setText(MessageManager @@ -1726,7 +1736,7 @@ public class GAlignFrame extends JInternalFrame modifyPID.setText(MessageManager .getString("label.modify_identity_thereshold")); - modifyPID.addActionListener(new java.awt.event.ActionListener() + modifyPID.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -1736,15 +1746,14 @@ public class GAlignFrame extends JInternalFrame }); modifyConservation.setText(MessageManager .getString("label.modify_conservation_thereshold")); - modifyConservation - .addActionListener(new java.awt.event.ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - modifyConservation_actionPerformed(e); - } - }); + modifyConservation.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + modifyConservation_actionPerformed(e); + } + }); sortByTreeMenu .setText(MessageManager.getString("action.by_tree_order")); sort.setText(MessageManager.getString("action.sort")); @@ -1825,6 +1834,55 @@ public class GAlignFrame extends JInternalFrame showTranslation_actionPerformed(e); } }); + + /* + * cDNA menu options + */ + cdna.setText(MessageManager.getString("label.cdna")); + // link to available cDNA + JMenuItem linkCdna = new JMenuItem( + MessageManager.getString("label.link_cdna")); + linkCdna.setToolTipText(JvSwingUtils.wrapTooltip(true, + MessageManager.getString("label.link_cdna_tip"))); + linkCdna.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + linkCdna_actionPerformed(); + } + }); + cdna.add(linkCdna); + // align linked cDNA + JMenuItem alignCdna = new JMenuItem( + MessageManager.getString("label.align_cdna")); + alignCdna.setToolTipText(JvSwingUtils.wrapTooltip(true, + MessageManager.getString("label.align_cdna_tip"))); + alignCdna.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + alignCdna_actionPerformed(); + } + }); + cdna.add(alignCdna); + + // view alignment as cDNA (when known) + JMenuItem viewAsCdna = new JMenuItem( + MessageManager.getString("label.view_as_cdna")); + viewAsCdna.setToolTipText(JvSwingUtils.wrapTooltip(true, + MessageManager.getString("label.view_as_cdna_tip"))); + viewAsCdna.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + viewAsCdna_actionPerformed(); + } + }); + cdna.add(viewAsCdna); + extractScores.setText(MessageManager.getString("label.extract_scores") + "..."); extractScores.addActionListener(new ActionListener() @@ -1835,15 +1893,11 @@ public class GAlignFrame extends JInternalFrame extractScores_actionPerformed(e); } }); - extractScores.setVisible(true); // JBPNote: TODO: make gui for regex based - // score extraction + extractScores.setVisible(true); + // JBPNote: TODO: make gui for regex based score extraction + + // for show products actions see AlignFrame.canShowProducts showProducts.setText(MessageManager.getString("label.get_cross_refs")); - /* - * showProducts.addActionListener(new ActionListener() { - * - * public void actionPerformed(ActionEvent e) { - * showProducts_actionPerformed(e); } }); - */ openFeatureSettings.setText(MessageManager .getString("label.feature_settings")); openFeatureSettings.addActionListener(new ActionListener() @@ -2094,20 +2148,22 @@ public class GAlignFrame extends JInternalFrame hiddenMarkers_actionPerformed(e); } }); + invertColSel.setText(MessageManager .getString("action.invert_column_selection")); - invertColSel.setAccelerator(javax.swing.KeyStroke.getKeyStroke( - java.awt.event.KeyEvent.VK_I, Toolkit.getDefaultToolkit() - .getMenuShortcutKeyMask() - | java.awt.event.KeyEvent.ALT_MASK, false)); - invertColSel.addActionListener(new ActionListener() + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_I, + Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() + | KeyEvent.ALT_MASK, false); + al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { invertColSel_actionPerformed(e); } - }); + }; + addMenuActionAndAccelerator(keyStroke, invertColSel, al); + tabbedPane.addChangeListener(new javax.swing.event.ChangeListener() { @Override @@ -2134,18 +2190,20 @@ public class GAlignFrame extends JInternalFrame tabbedPane_focusGained(e); } }); + save.setText(MessageManager.getString("action.save")); - save.setAccelerator(javax.swing.KeyStroke.getKeyStroke( - java.awt.event.KeyEvent.VK_S, Toolkit.getDefaultToolkit() - .getMenuShortcutKeyMask(), false)); - save.addActionListener(new ActionListener() + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_S, Toolkit + .getDefaultToolkit().getMenuShortcutKeyMask(), false); + al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { save_actionPerformed(e); } - }); + }; + addMenuActionAndAccelerator(keyStroke, save, al); + reload.setEnabled(false); reload.setText(MessageManager.getString("action.reload")); reload.addActionListener(new ActionListener() @@ -2156,18 +2214,20 @@ public class GAlignFrame extends JInternalFrame reload_actionPerformed(e); } }); + newView.setText(MessageManager.getString("action.new_view")); - newView.setAccelerator(javax.swing.KeyStroke.getKeyStroke( - java.awt.event.KeyEvent.VK_T, Toolkit.getDefaultToolkit() - .getMenuShortcutKeyMask(), false)); - newView.addActionListener(new ActionListener() + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_T, Toolkit + .getDefaultToolkit().getMenuShortcutKeyMask(), false); + al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { newView_actionPerformed(e); } - }); + }; + addMenuActionAndAccelerator(keyStroke, newView, al); + tabbedPane.setToolTipText("" + MessageManager.getString("label.rename_tab_eXpand_reGroup") + ""); @@ -2193,30 +2253,33 @@ public class GAlignFrame extends JInternalFrame idRightAlign_actionPerformed(e); } }); + gatherViews.setEnabled(false); gatherViews.setText(MessageManager.getString("action.gather_views")); - gatherViews.setAccelerator(javax.swing.KeyStroke.getKeyStroke( - java.awt.event.KeyEvent.VK_G, 0, false)); - gatherViews.addActionListener(new ActionListener() + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_G, 0, false); + al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { gatherViews_actionPerformed(e); } - }); + }; + addMenuActionAndAccelerator(keyStroke, gatherViews, al); + expandViews.setEnabled(false); expandViews.setText(MessageManager.getString("action.expand_views")); - expandViews.setAccelerator(javax.swing.KeyStroke.getKeyStroke( - java.awt.event.KeyEvent.VK_X, 0, false)); - expandViews.addActionListener(new ActionListener() + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_X, 0, false); + al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { expandViews_actionPerformed(e); } - }); + }; + addMenuActionAndAccelerator(keyStroke, expandViews, al); + pageSetup .setText(MessageManager.getString("action.page_setup") + "..."); pageSetup.addActionListener(new ActionListener() @@ -2362,6 +2425,7 @@ public class GAlignFrame extends JInternalFrame calculateMenu.add(PCAMenuItem); calculateMenu.addSeparator(); calculateMenu.add(showTranslation); + calculateMenu.add(cdna); calculateMenu.add(showProducts); calculateMenu.add(autoCalculate); calculateMenu.add(sortByTree); @@ -2430,6 +2494,34 @@ public class GAlignFrame extends JInternalFrame } /** + * Adds the given action listener and key accelerator to the given menu item. + * Also saves in a lookup table to support lookup of action by key stroke. + * + * @param keyStroke + * @param menuItem + * @param actionListener + */ + protected void addMenuActionAndAccelerator(KeyStroke keyStroke, + JMenuItem menuItem, ActionListener actionListener) + { + menuItem.setAccelerator(keyStroke); + accelerators.put(keyStroke, menuItem); + menuItem.addActionListener(actionListener); + } + + protected void viewAsCdna_actionPerformed() + { + } + + protected void alignCdna_actionPerformed() + { + } + + protected void linkCdna_actionPerformed() + { + } + + /** * Action on clicking sort annotations by type. * * @param sortOrder @@ -2595,10 +2687,6 @@ public class GAlignFrame extends JInternalFrame { } - protected void showProducts_actionPerformed(ActionEvent e) - { - } - protected void buildSortByAnnotationScoresMenu() { } @@ -3160,4 +3248,9 @@ public class GAlignFrame extends JInternalFrame { this.annotationSortOrder = annotationSortOrder; } + + public Map getAccelerators() + { + return this.accelerators; + } } diff --git a/src/jalview/jbgui/GSplitFrame.java b/src/jalview/jbgui/GSplitFrame.java new file mode 100644 index 0000000..281a93e --- /dev/null +++ b/src/jalview/jbgui/GSplitFrame.java @@ -0,0 +1,43 @@ +package jalview.jbgui; + +import javax.swing.JComponent; +import javax.swing.JInternalFrame; +import javax.swing.JSplitPane; + +public class GSplitFrame extends JInternalFrame +{ + private static final long serialVersionUID = 1L; + + private JComponent topComponent; + + private JComponent bottomComponent; + + /** + * Constructor + * + * @param top + * @param bottom + */ + public GSplitFrame(JComponent top, JComponent bottom) + { + this.topComponent = top; + this.bottomComponent = bottom; + JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, top, + bottom); + splitPane.setVisible(true); + add(splitPane); + splitPane.setDividerLocation(0.5d); + splitPane.setResizeWeight(0.5d); + splitPane.setDividerSize(0); + } + + public JComponent getTopComponent() + { + return topComponent; + } + + public JComponent getBottomComponent() + { + return bottomComponent; + } +} diff --git a/src/jalview/jbgui/GTreePanel.java b/src/jalview/jbgui/GTreePanel.java index b492c21..83a9866 100755 --- a/src/jalview/jbgui/GTreePanel.java +++ b/src/jalview/jbgui/GTreePanel.java @@ -22,10 +22,19 @@ package jalview.jbgui; import jalview.util.MessageManager; -import java.awt.*; -import java.awt.event.*; -import javax.swing.*; -import javax.swing.event.*; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JInternalFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JScrollPane; +import javax.swing.event.MenuEvent; +import javax.swing.event.MenuListener; public class GTreePanel extends JInternalFrame { @@ -124,7 +133,7 @@ public class GTreePanel extends JInternalFrame { public void actionPerformed(ActionEvent e) { - sortByTree_actionPerformed(e); + sortByTree_actionPerformed(); } }); font.setText(MessageManager.getString("action.font")); @@ -282,7 +291,7 @@ public class GTreePanel extends JInternalFrame { } - public void sortByTree_actionPerformed(ActionEvent e) + public void sortByTree_actionPerformed() { } diff --git a/src/jalview/schemes/ResidueProperties.java b/src/jalview/schemes/ResidueProperties.java index d13f0a9..4bb246a 100755 --- a/src/jalview/schemes/ResidueProperties.java +++ b/src/jalview/schemes/ResidueProperties.java @@ -27,6 +27,7 @@ import jalview.api.analysis.ScoreModelI; import java.awt.Color; import java.util.ArrayList; import java.util.Enumeration; +import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.Map; @@ -43,11 +44,11 @@ public class ResidueProperties public static final int[] purinepyrimidineIndex; - public static final Hashtable aa3Hash = new Hashtable(); + public static final Map aa3Hash = new HashMap(); - public static final Hashtable aa2Triplet = new Hashtable(); + public static final Map aa2Triplet = new HashMap(); - public static final Hashtable nucleotideName = new Hashtable(); + public static final Map nucleotideName = new HashMap(); static { @@ -676,6 +677,8 @@ public class ResidueProperties public static Vector STOP = new Vector(); + public static String START = "ATG"; + static { codonHash.put("K", Lys); @@ -1521,7 +1524,7 @@ public class ResidueProperties return hyd; } - public static Hashtable getAA3Hash() + public static Map getAA3Hash() { return aa3Hash; } diff --git a/src/jalview/structure/AtomSpec.java b/src/jalview/structure/AtomSpec.java new file mode 100644 index 0000000..d3e8d42 --- /dev/null +++ b/src/jalview/structure/AtomSpec.java @@ -0,0 +1,64 @@ +package jalview.structure; + +/** + * Java bean representing an atom in a PDB (or similar) structure model. + * + * @author gmcarstairs + * + */ +public class AtomSpec +{ + // TODO clarify do we want pdbFile here, or pdbId? + // compare highlightAtom in 2.8.2 for JalviewJmolBinding and + // javascript.MouseOverStructureListener + private String pdbFile; + + private String chain; + + private int pdbResNum; + + private int atomIndex; + + /** + * Constructor + * + * @param pdbFile + * @param chain + * @param resNo + * @param atomNo + */ + public AtomSpec(String pdbFile, String chain, int resNo, int atomNo) + { + this.pdbFile = pdbFile; + this.chain = chain; + this.pdbResNum = resNo; + this.atomIndex = atomNo; + } + + public String getPdbFile() + { + return pdbFile; + } + + public String getChain() + { + return chain; + } + + public int getPdbResNum() + { + return pdbResNum; + } + + public int getAtomIndex() + { + return atomIndex; + } + + @Override + public String toString() + { + return "pdbFile: " + pdbFile + ", chain: " + chain + ", res: " + + pdbResNum + ", atom: " + atomIndex; + } +} diff --git a/src/jalview/structure/CommandListener.java b/src/jalview/structure/CommandListener.java new file mode 100644 index 0000000..e5f3e36 --- /dev/null +++ b/src/jalview/structure/CommandListener.java @@ -0,0 +1,35 @@ +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 + * @param source + * the originator of the command + */ + public void mirrorCommand(CommandI command, boolean undo, + StructureSelectionManager ssm, VamsasSource source); + + /** + * Temporary workaround to make check for source == listener work. + * + * @return + */ + public VamsasSource getVamsasSource(); +} diff --git a/src/jalview/structure/SequenceListener.java b/src/jalview/structure/SequenceListener.java index 83a9fd0..24c11e5 100644 --- a/src/jalview/structure/SequenceListener.java +++ b/src/jalview/structure/SequenceListener.java @@ -20,7 +20,8 @@ */ package jalview.structure; -import jalview.datamodel.*; +import jalview.datamodel.SequenceI; + public interface SequenceListener { @@ -29,4 +30,7 @@ public interface SequenceListener public void highlightSequence(jalview.datamodel.SearchResults results); public void updateColours(SequenceI sequence, int index); + + public VamsasSource getVamsasSource(); + } diff --git a/src/jalview/structure/StructureListener.java b/src/jalview/structure/StructureListener.java index 8ac002b..032c40b 100644 --- a/src/jalview/structure/StructureListener.java +++ b/src/jalview/structure/StructureListener.java @@ -20,40 +20,27 @@ */ 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 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 @@ -62,19 +49,7 @@ public interface StructureListener 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). * diff --git a/src/jalview/structure/StructureSelectionManager.java b/src/jalview/structure/StructureSelectionManager.java index af00798..eb9abab 100644 --- a/src/jalview/structure/StructureSelectionManager.java +++ b/src/jalview/structure/StructureSelectionManager.java @@ -22,19 +22,29 @@ package jalview.structure; import jalview.analysis.AlignSeq; import jalview.api.StructureSelectionManagerProvider; +import jalview.commands.CommandI; +import jalview.commands.EditCommand; +import jalview.commands.OrderCommand; import jalview.datamodel.AlignedCodonFrame; import jalview.datamodel.AlignmentAnnotation; +import jalview.datamodel.AlignmentI; import jalview.datamodel.Annotation; import jalview.datamodel.PDBEntry; import jalview.datamodel.SearchResults; import jalview.datamodel.SequenceI; import jalview.io.AppletFormatAdapter; +import jalview.util.MappingUtils; import jalview.util.MessageManager; import java.io.PrintStream; +import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.IdentityHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.Vector; import MCview.Atom; @@ -44,10 +54,28 @@ public class StructureSelectionManager { static IdentityHashMap instances; - StructureMapping[] mappings; + private List mappings = new ArrayList(); - private boolean processSecondaryStructure = false, - secStructServices = false, addTempFacAnnot = false; + private boolean processSecondaryStructure = false; + + private boolean secStructServices = false; + + private boolean addTempFacAnnot = false; + + /* + * Set of any registered mappings between (dataset) sequences. + */ + Set seqmappings = new LinkedHashSet(); + + /* + * Reference counters for the above mappings. Remove mappings when ref count + * goes to zero. + */ + Map seqMappingRefCounts = new HashMap(); + + private List commandListeners = new ArrayList(); + + private List sel_listeners = new ArrayList(); /** * @return true if will try to use external services for processing secondary @@ -115,17 +143,18 @@ public class StructureSelectionManager */ public void reportMapping() { - if (mappings == null) + if (mappings.isEmpty()) { System.err.println("reportMapping: No PDB/Sequence mappings."); } else { - System.err.println("reportMapping: There are " + mappings.length + System.err.println("reportMapping: There are " + mappings.size() + " mappings."); - for (int m = 0; m < mappings.length; m++) + int i = 0; + for (StructureMapping sm : mappings) { - System.err.println("mapping " + m + " : " + mappings[m].pdbfile); + System.err.println("mapping " + i++ + " : " + sm.pdbfile); } } } @@ -246,16 +275,19 @@ public class StructureSelectionManager } } + /** + * Returns the file name for a mapped PDB id (or null if not mapped). + * + * @param pdbid + * @return + */ public String alreadyMappedToFile(String pdbid) { - if (mappings != null) + for (StructureMapping sm : mappings) { - for (int i = 0; i < mappings.length; i++) + if (sm.getPdbId().equals(pdbid)) { - if (mappings[i].getPdbId().equals(pdbid)) - { - return mappings[i].pdbfile; - } + return sm.pdbfile; } } return null; @@ -482,19 +514,7 @@ public class StructureSelectionManager mappingDetails.toString()); if (forStructureView) { - - if (mappings == null) - { - mappings = new StructureMapping[1]; - } - else - { - StructureMapping[] tmp = new StructureMapping[mappings.length + 1]; - System.arraycopy(mappings, 0, tmp, 0, mappings.length); - mappings = tmp; - } - - mappings[mappings.length - 1] = newMapping; + mappings.add(newMapping); } maxChain.transferResidueAnnotation(newMapping, sqmpping); } @@ -546,19 +566,18 @@ public class StructureSelectionManager } } - if (pdbs.size() > 0 && mappings != null) + if (pdbs.size() > 0) { - Vector tmp = new Vector(); - for (int i = 0; i < mappings.length; i++) + List tmp = new ArrayList(); + for (StructureMapping sm : mappings) { - if (!pdbs.contains(mappings[i].pdbfile)) + if (!pdbs.contains(sm.pdbfile)) { - tmp.addElement(mappings[i]); + tmp.add(sm); } } - mappings = new StructureMapping[tmp.size()]; - tmp.copyInto(mappings); + mappings = tmp; } } @@ -569,7 +588,6 @@ public class StructureSelectionManager // old or prematurely sent event return; } - boolean hasSequenceListeners = handlingVamsasMo || seqmappings != null; SearchResults results = null; SequenceI lastseq = null; int lastipos = -1, indexpos; @@ -581,33 +599,21 @@ public class StructureSelectionManager { results = new SearchResults(); } - if (mappings != null) + for (StructureMapping sm : mappings) { - for (int j = 0; j < mappings.length; j++) + if (sm.pdbfile.equals(pdbfile) && sm.pdbchain.equals(chain)) { - if (mappings[j].pdbfile.equals(pdbfile) - && mappings[j].pdbchain.equals(chain)) + indexpos = sm.getSeqPos(pdbResNum); + if (lastipos != indexpos && lastseq != sm.sequence) { - indexpos = mappings[j].getSeqPos(pdbResNum); - if (lastipos != indexpos && lastseq != mappings[j].sequence) + results.addResult(sm.sequence, indexpos, indexpos); + lastipos = indexpos; + lastseq = sm.sequence; + // construct highlighted sequence list + for (AlignedCodonFrame acf : seqmappings) { - results.addResult(mappings[j].sequence, indexpos, indexpos); - lastipos = indexpos; - lastseq = mappings[j].sequence; - // construct highlighted sequence list - if (seqmappings != null) - { - - Enumeration e = seqmappings.elements(); - while (e.hasMoreElements()) - - { - ((AlignedCodonFrame) e.nextElement()).markMappedRegion( - mappings[j].sequence, indexpos, results); - } - } + acf.markMappedRegion(sm.sequence, indexpos, results); } - } } } @@ -626,13 +632,11 @@ public class StructureSelectionManager } } - 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 @@ -642,93 +646,54 @@ public class StructureSelectionManager public void mouseOverSequence(SequenceI seq, int indexpos, int index, VamsasSource source) { - boolean hasSequenceListeners = handlingVamsasMo || seqmappings != null; + boolean hasSequenceListeners = handlingVamsasMo + || !seqmappings.isEmpty(); SearchResults results = null; if (index == -1) { 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) + if (results == null) { - Enumeration e = seqmappings.elements(); - while (e.hasMoreElements()) - - { - ((AlignedCodonFrame) e.nextElement()).markMappedRegion( - seq, index, results); - } + results = MappingUtils.buildSearchResults(seq, index, + seqmappings); } - // hasSequenceListeners = results.getSize() > 0; if (handlingVamsasMo) { - // maybe have to resolve seq to a dataset seqeunce... - // add in additional direct sequence and/or dataset sequence - // highlighting results.addResult(seq, index, 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) { @@ -740,6 +705,35 @@ public class StructureSelectionManager } /** + * 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; + List atoms = new ArrayList(); + for (StructureMapping sm : mappings) + { + if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()) + { + atomNo = sm.getAtomNum(index); + + if (atomNo > 0) + { + atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain, sm + .getPDBResNum(index), atomNo)); + } + } + } + sl.highlightAtoms(atoms); + } + + /** * true if a mouse over event from an external (ie Vamsas) source is being * handled */ @@ -825,119 +819,114 @@ public class StructureSelectionManager public StructureMapping[] getMapping(String pdbfile) { - Vector tmp = new Vector(); - if (mappings != null) - { - for (int i = 0; i < mappings.length; i++) + List tmp = new ArrayList(); + for (StructureMapping sm : mappings) { - if (mappings[i].pdbfile.equals(pdbfile)) + if (sm.pdbfile.equals(pdbfile)) { - tmp.addElement(mappings[i]); + tmp.add(sm); } - } } - StructureMapping[] ret = new StructureMapping[tmp.size()]; - for (int i = 0; i < tmp.size(); i++) - { - ret[i] = (StructureMapping) tmp.elementAt(i); - } - - return ret; + return tmp.toArray(new StructureMapping[tmp.size()]); } public String printMapping(String pdbfile) { - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < mappings.length; i++) + StringBuilder sb = new StringBuilder(64); + for (StructureMapping sm : mappings) { - if (mappings[i].pdbfile.equals(pdbfile)) + if (sm.pdbfile.equals(pdbfile)) { - sb.append(mappings[i].mappingDetails); + sb.append(sm.mappingDetails); } } return sb.toString(); } - private int[] seqmappingrefs = null; // refcount for seqmappings elements - - private synchronized void modifySeqMappingList(boolean add, - AlignedCodonFrame[] codonFrames) + /** + * Decrement the reference counter for each of the given mappings, and remove + * it entirely if its reference counter reduces to zero. + * + * @param set + */ + public void removeMappings(Set set) { - if (!add && (seqmappings == null || seqmappings.size() == 0)) - { - return; - } - if (seqmappings == null) + if (set != null) { - seqmappings = new Vector(); + for (AlignedCodonFrame acf : set) + { + removeMapping(acf); + } } - if (codonFrames != null && codonFrames.length > 0) + } + + /** + * Decrement the reference counter for the given mapping, and remove it + * entirely if its reference counter reduces to zero. + * + * @param acf + */ + public void removeMapping(AlignedCodonFrame acf) + { + if (acf != null && seqmappings.contains(acf)) { - for (int cf = 0; cf < codonFrames.length; cf++) + int count = seqMappingRefCounts.get(acf); + count--; + if (count > 0) { - if (seqmappings.contains(codonFrames[cf])) - { - if (add) - { - seqmappingrefs[seqmappings.indexOf(codonFrames[cf])]++; - } - else - { - if (--seqmappingrefs[seqmappings.indexOf(codonFrames[cf])] <= 0) - { - int pos = seqmappings.indexOf(codonFrames[cf]); - int[] nr = new int[seqmappingrefs.length - 1]; - if (pos > 0) - { - System.arraycopy(seqmappingrefs, 0, nr, 0, pos); - } - if (pos < seqmappingrefs.length - 1) - { - System.arraycopy(seqmappingrefs, pos + 1, nr, 0, - seqmappingrefs.length - pos - 2); - } - } - } - } - else - { - if (add) - { - seqmappings.addElement(codonFrames[cf]); - - int[] nsr = new int[(seqmappingrefs == null) ? 1 - : seqmappingrefs.length + 1]; - if (seqmappingrefs != null && seqmappingrefs.length > 0) - { - System.arraycopy(seqmappingrefs, 0, nsr, 0, - seqmappingrefs.length); - } - nsr[(seqmappingrefs == null) ? 0 : seqmappingrefs.length] = 1; - seqmappingrefs = nsr; - } - } + seqMappingRefCounts.put(acf, count); + } + else + { + seqmappings.remove(acf); + seqMappingRefCounts.remove(acf); } } } - public void removeMappings(AlignedCodonFrame[] codonFrames) + /** + * Add each of the given codonFrames to the stored set. If not aready present, + * increments its reference count instead. + * + * @param set + */ + public void addMappings(Set set) { - modifySeqMappingList(false, codonFrames); + if (set != null) + { + for (AlignedCodonFrame acf : set) + { + addMapping(acf); + } + } } - public void addMappings(AlignedCodonFrame[] codonFrames) + /** + * Add the given mapping to the stored set, or if already stored, increment + * its reference counter. + */ + public void addMapping(AlignedCodonFrame acf) { - modifySeqMappingList(true, codonFrames); + if (acf != null) + { + if (seqmappings.contains(acf)) + { + seqMappingRefCounts.put(acf, seqMappingRefCounts.get(acf) + 1); + } + else + { + seqmappings.add(acf); + seqMappingRefCounts.put(acf, 1); + } + } } - Vector sel_listeners = new Vector(); - public void addSelectionListener(SelectionListener selecter) { if (!sel_listeners.contains(selecter)) { - sel_listeners.addElement(selecter); + sel_listeners.add(selecter); } } @@ -945,7 +934,7 @@ public class StructureSelectionManager { if (sel_listeners.contains(toremove)) { - sel_listeners.removeElement(toremove); + sel_listeners.remove(toremove); } } @@ -953,18 +942,11 @@ public class StructureSelectionManager jalview.datamodel.SequenceGroup selection, jalview.datamodel.ColumnSelection colsel, SelectionSource source) { - if (sel_listeners != null && sel_listeners.size() > 0) + for (SelectionListener slis : sel_listeners) { - Enumeration listeners = sel_listeners.elements(); - while (listeners.hasMoreElements()) + if (slis != source) { - SelectionListener slis = ((SelectionListener) listeners - .nextElement()); - if (slis != source) - { - slis.selection(selection, colsel, source); - } - ; + slis.selection(selection, colsel, source); } } } @@ -992,32 +974,6 @@ public class StructureSelectionManager } } - public void finalize() throws Throwable - { - if (listeners != null) - { - listeners.clear(); - listeners = null; - } - if (pdbIdFileName != null) - { - pdbIdFileName.clear(); - pdbIdFileName = null; - } - if (sel_listeners != null) - { - sel_listeners.clear(); - sel_listeners = null; - } - if (view_listeners != null) - { - view_listeners.clear(); - view_listeners = null; - } - mappings = null; - seqmappingrefs = null; - } - /** * release all references associated with this manager provider * @@ -1041,7 +997,6 @@ public class StructureSelectionManager } catch (Throwable x) { } - ; } } } @@ -1055,4 +1010,67 @@ public class StructureSelectionManager } } + 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) + { + listener.mirrorCommand(command, undo, this, source); + } + } + + /** + * Returns a new CommandI representing the given command as mapped to the + * given sequences. If no mapping could be made, or the command is not of a + * mappable kind, returns null. + * + * @param command + * @param undo + * @param mapTo + * @param gapChar + * @return + */ + public CommandI mapCommand(CommandI command, boolean undo, + final AlignmentI mapTo, char gapChar) + { + if (command instanceof EditCommand) + { + return MappingUtils.mapEditCommand((EditCommand) command, undo, + mapTo, gapChar, seqmappings); + } + else if (command instanceof OrderCommand) + { + return MappingUtils.mapOrderCommand((OrderCommand) command, undo, + mapTo, seqmappings); + } + return null; + } } diff --git a/src/jalview/structure/VamsasListener.java b/src/jalview/structure/VamsasListener.java index 26ebdc1..1c49252 100644 --- a/src/jalview/structure/VamsasListener.java +++ b/src/jalview/structure/VamsasListener.java @@ -36,5 +36,6 @@ import jalview.datamodel.SequenceI; */ public interface VamsasListener { - public void mouseOver(SequenceI seq, int index, VamsasSource source); + public void mouseOverSequence(SequenceI seq, int index, + VamsasSource source); } diff --git a/src/jalview/structures/models/AAStructureBindingModel.java b/src/jalview/structures/models/AAStructureBindingModel.java index 664c903..653ec2d 100644 --- a/src/jalview/structures/models/AAStructureBindingModel.java +++ b/src/jalview/structures/models/AAStructureBindingModel.java @@ -3,15 +3,16 @@ package jalview.structures.models; import jalview.api.StructureSelectionManagerProvider; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; +import jalview.structure.AtomSpec; import jalview.structure.StructureListener; import jalview.structure.StructureSelectionManager; import jalview.util.MessageManager; -import java.awt.event.ComponentEvent; import java.util.ArrayList; import java.util.List; /** + * * A base class to hold common function for protein structure model binding. * Initial version created by refactoring JMol and Chimera binding models, but * other structure viewers could in principle be accommodated in future. @@ -361,4 +362,21 @@ public abstract class AAStructureBindingModel extends } } + @Override + public void highlightAtoms(List atoms) + { + if (atoms != null) + { + for (AtomSpec atom : atoms) + { + highlightAtom(atom.getAtomIndex(), atom.getPdbResNum(), + atom.getChain(), atom.getPdbFile()); + } + } + } + + // TODO Jmol and Chimera seem to expect pdbFile, javascript listener pdbId + protected abstract void highlightAtom(int atomIndex, int pdbResNum, + String chain, String pdbFile); + } \ No newline at end of file diff --git a/src/jalview/util/Comparison.java b/src/jalview/util/Comparison.java index 8931b23..b9a95be 100644 --- a/src/jalview/util/Comparison.java +++ b/src/jalview/util/Comparison.java @@ -20,18 +20,25 @@ */ 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!! */ - public static final String GapChars = " .-"; + private static final int EIGHTY_FIVE = 85; + + private static final int TO_UPPER_CASE = 'a' - 'A'; + + private static final char GAP_SPACE = ' '; + + private static final char GAP_DOT = '.'; + + private static final char GAP_DASH = '-'; + + public static final String GapChars = new String(new char[] + { GAP_SPACE, GAP_DOT, GAP_DASH }); /** * DOCUMENT ME! @@ -69,12 +76,12 @@ public class Comparison 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--; } @@ -225,47 +232,60 @@ public class Comparison } /** - * 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; + return (c == GAP_DASH || c == GAP_DOT || c == GAP_SPACE) ? 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; } diff --git a/src/jalview/util/MapList.java b/src/jalview/util/MapList.java index eb414b9..32f0256 100644 --- a/src/jalview/util/MapList.java +++ b/src/jalview/util/MapList.java @@ -20,86 +20,119 @@ */ package jalview.util; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; /** - * MapList Simple way of bijectively mapping a non-contiguous linear range to - * another non-contiguous linear range Use at your own risk! TODO: efficient - * implementation of private posMap method TODO: test/ensure that sense of from - * and to ratio start position is conserved (codon start position recovery) - * TODO: optimize to use int[][] arrays rather than vectors. + * A simple way of bijectively mapping a non-contiguous linear range to another + * non-contiguous linear range. + * + * Use at your own risk! + * + * TODO: efficient implementation of private posMap method + * + * TODO: test/ensure that sense of from and to ratio start position is conserved + * (codon start position recovery) */ public class MapList { + /* - * (non-Javadoc) - * - * @see java.lang.Object#equals(java.lang.Object) + * Subregions (base 1) described as { [start1, end1], [start2, end2], ...} + */ + private List fromShifts = new ArrayList(); + + /* + * Same format as fromShifts, for the 'mapped to' sequence + */ + private List toShifts = new ArrayList(); + + /* + * number of steps in fromShifts to one toRatio unit + */ + private int fromRatio; + + /* + * number of steps in toShifts to one fromRatio + */ + private int toRatio; + + /* + * lowest and highest value in the from Map */ - public boolean equals(MapList obj) + private int fromLowest; + + private int fromHighest; + + /* + * lowest and highest value in the to Map + */ + private int toLowest; + + private int toHighest; + + /** + * Two MapList objects are equal if they are the same object, or they both + * have populated shift ranges and all values are the same. + */ + @Override + public boolean equals(Object o) { + if (o == null || !(o instanceof MapList)) + { + return false; + } + + MapList obj = (MapList) o; if (obj == this) - return true; - if (obj != null && obj.fromRatio == fromRatio && obj.toRatio == toRatio - && obj.fromShifts != null && obj.toShifts != null) { - int i, iSize = fromShifts.size(), j, jSize = obj.fromShifts.size(); - if (iSize != jSize) - return false; - for (i = 0, iSize = fromShifts.size(), j = 0, jSize = obj.fromShifts - .size(); i < iSize;) - { - int[] mi = (int[]) fromShifts.elementAt(i++); - int[] mj = (int[]) obj.fromShifts.elementAt(j++); - if (mi[0] != mj[0] || mi[1] != mj[1]) - return false; - } - iSize = toShifts.size(); - jSize = obj.toShifts.size(); - if (iSize != jSize) - return false; - for (i = 0, j = 0; i < iSize;) - { - int[] mi = (int[]) toShifts.elementAt(i++); - int[] mj = (int[]) obj.toShifts.elementAt(j++); - if (mi[0] != mj[0] || mi[1] != mj[1]) - return false; - } return true; } - return false; + if (obj.fromRatio != fromRatio || obj.toRatio != toRatio + || obj.fromShifts == null || obj.toShifts == null) + { + return false; + } + return Arrays + .deepEquals(fromShifts.toArray(), obj.fromShifts.toArray()) + && Arrays + .deepEquals(toShifts.toArray(), obj.toShifts.toArray()); } - public Vector fromShifts; - - public Vector toShifts; - - int fromRatio; // number of steps in fromShifts to one toRatio unit - - int toRatio; // number of steps in toShifts to one fromRatio - /** + * Returns the flattened 'from' ranges as [start1, end1, start2, end2, ...] * - * @return series of intervals mapped in from + * @return */ public int[] getFromRanges() { return getRanges(fromShifts); } + /** + * Returns the flattened 'to' ranges as [start1, end1, start2, end2, ...] + * + * @return + */ public int[] getToRanges() { return getRanges(toShifts); } - private int[] getRanges(Vector shifts) + /** + * Flattens a list of [start, end] into a single [start1, end1, start2, + * end2,...] array. + * + * @param shifts + * @return + */ + protected static int[] getRanges(List shifts) { int[] rnges = new int[2 * shifts.size()]; - Enumeration e = shifts.elements(); int i = 0; - while (e.hasMoreElements()) + for (int[] r : shifts) { - int r[] = (int[]) e.nextElement(); rnges[i++] = r[0]; rnges[i++] = r[1]; } @@ -107,16 +140,6 @@ public class MapList } /** - * lowest and highest value in the from Map - */ - int[] fromRange = null; - - /** - * lowest and highest value in the to Map - */ - int[] toRange = null; - - /** * * @return length of mapped phrase in from */ @@ -136,88 +159,91 @@ public class MapList public int getFromLowest() { - return fromRange[0]; + return fromLowest; } public int getFromHighest() { - return fromRange[1]; + return fromHighest; } public int getToLowest() { - return toRange[0]; + return toLowest; } public int getToHighest() { - return toRange[1]; - } - - private void ensureRange(int[] limits, int pos) - { - if (limits[0] > pos) - limits[0] = pos; - if (limits[1] < pos) - limits[1] = pos; + return toHighest; } + /** + * Constructor. + * + * @param from + * contiguous regions as [start1, end1, start2, end2, ...] + * @param to + * same format as 'from' + * @param fromRatio + * phrase length in 'from' (e.g. 3 for dna) + * @param toRatio + * phrase length in 'to' (e.g. 1 for protein) + */ public MapList(int from[], int to[], int fromRatio, int toRatio) { - fromRange = new int[] - { from[0], from[1] }; - toRange = new int[] - { to[0], to[1] }; - - fromShifts = new Vector(); + fromLowest = from[0]; + fromHighest = from[1]; for (int i = 0; i < from.length; i += 2) { - ensureRange(fromRange, from[i]); - ensureRange(fromRange, from[i + 1]); + fromLowest = Math.min(fromLowest, from[i]); + fromHighest = Math.max(fromHighest, from[i + 1]); - fromShifts.addElement(new int[] + fromShifts.add(new int[] { from[i], from[i + 1] }); } - toShifts = new Vector(); + + toLowest = to[0]; + toHighest = to[1]; for (int i = 0; i < to.length; i += 2) { - ensureRange(toRange, to[i]); - ensureRange(toRange, to[i + 1]); - toShifts.addElement(new int[] + toLowest = Math.min(toLowest, to[i]); + toHighest = Math.max(toHighest, to[i + 1]); + toShifts.add(new int[] { to[i], to[i + 1] }); } this.fromRatio = fromRatio; this.toRatio = toRatio; } + /** + * Copy constructor. Creates an identical mapping. + * + * @param map + */ public MapList(MapList map) { - this.fromRange = new int[] - { map.fromRange[0], map.fromRange[1] }; - this.toRange = new int[] - { map.toRange[0], map.toRange[1] }; + // TODO not used - remove? + this.fromLowest = map.fromLowest; + this.fromHighest = map.fromHighest; + this.toLowest = map.toLowest; + this.toHighest = map.toHighest; + this.fromRatio = map.fromRatio; this.toRatio = map.toRatio; if (map.fromShifts != null) { - this.fromShifts = new Vector(); - Enumeration e = map.fromShifts.elements(); - while (e.hasMoreElements()) + for (int[] r : map.fromShifts) { - int[] el = (int[]) e.nextElement(); - fromShifts.addElement(new int[] - { el[0], el[1] }); + fromShifts.add(new int[] + { r[0], r[1] }); } } if (map.toShifts != null) { - this.toShifts = new Vector(); - Enumeration e = map.toShifts.elements(); - while (e.hasMoreElements()) + for (int[] r : map.toShifts) { - int[] el = (int[]) e.nextElement(); - toShifts.addElement(new int[] - { el[0], el[1] }); + toShifts.add(new int[] + { r[0], r[1] }); } } } @@ -228,8 +254,9 @@ public class MapList * @return int[][] { int[] { fromStart, fromFinish, toStart, toFinish }, int * [fromFinish-fromStart+2] { toStart..toFinish mappings}} */ - public int[][] makeFromMap() + protected int[][] makeFromMap() { + // TODO not used - remove?? return posMap(fromShifts, fromRatio, toShifts, toRatio); } @@ -238,27 +265,30 @@ public class MapList * * @return int[to position]=position mapped in from */ - public int[][] makeToMap() + protected int[][] makeToMap() { + // TODO not used - remove?? return posMap(toShifts, toRatio, fromShifts, fromRatio); } /** * construct an int map for intervals in intVals * - * @param intVals + * @param shiftTo * @return int[] { from, to pos in range }, int[range.to-range.from+1] * returning mapped position */ - private int[][] posMap(Vector intVals, int ratio, Vector toIntVals, + private int[][] posMap(List shiftTo, int ratio, + List shiftFrom, int toRatio) { - int iv = 0, ivSize = intVals.size(); + // TODO not used - remove?? + int iv = 0, ivSize = shiftTo.size(); if (iv >= ivSize) { return null; } - int[] intv = (int[]) intVals.elementAt(iv++); + int[] intv = shiftTo.get(iv++); int from = intv[0], to = intv[1]; if (from > to) { @@ -267,7 +297,7 @@ public class MapList } while (iv < ivSize) { - intv = (int[]) intVals.elementAt(iv++); + intv = shiftTo.get(iv++); if (intv[0] < from) { from = intv[0]; @@ -289,7 +319,7 @@ public class MapList int mp[][] = new int[to - from + 2][]; for (int i = 0; i < mp.length; i++) { - int[] m = shift(i + from, intVals, ratio, toIntVals, toRatio); + int[] m = shift(i + from, shiftTo, ratio, shiftFrom, toRatio); if (m != null) { if (i == 0) @@ -345,6 +375,7 @@ public class MapList * shifts.insertElementAt(new int[] { pos, shift}, sidx); else * rshift[1]+=shift; } */ + /** * shift from pos to To(pos) * @@ -373,23 +404,24 @@ public class MapList /** * - * @param fromShifts + * @param shiftTo * @param fromRatio - * @param toShifts + * @param shiftFrom * @param toRatio * @return */ - private int[] shift(int pos, Vector fromShifts, int fromRatio, - Vector toShifts, int toRatio) + protected static int[] shift(int pos, List shiftTo, int fromRatio, + List shiftFrom, int toRatio) { - int[] fromCount = countPos(fromShifts, pos); + // TODO: javadoc; tests + int[] fromCount = countPos(shiftTo, pos); if (fromCount == null) { return null; } int fromRemainder = (fromCount[0] - 1) % fromRatio; int toCount = 1 + (((fromCount[0] - 1) / fromRatio) * toRatio); - int[] toPos = countToPos(toShifts, toCount); + int[] toPos = countToPos(shiftFrom, toCount); if (toPos == null) { return null; // throw new Error("Bad Mapping!"); @@ -402,16 +434,16 @@ public class MapList /** * count how many positions pos is along the series of intervals. * - * @param intVals + * @param shiftTo * @param pos * @return number of positions or null if pos is not within intervals */ - private int[] countPos(Vector intVals, int pos) + protected static int[] countPos(List shiftTo, int pos) { - int count = 0, intv[], iv = 0, ivSize = intVals.size(); + int count = 0, intv[], iv = 0, ivSize = shiftTo.size(); while (iv < ivSize) { - intv = (int[]) intVals.elementAt(iv++); + intv = shiftTo.get(iv++); if (intv[0] <= intv[1]) { if (pos >= intv[0] && pos <= intv[1]) @@ -443,17 +475,18 @@ public class MapList /** * count out pos positions into a series of intervals and return the position * - * @param intVals + * @param shiftFrom * @param pos * @return position pos in interval set */ - private int[] countToPos(Vector intVals, int pos) + protected static int[] countToPos(List shiftFrom, int pos) { - int count = 0, diff = 0, iv = 0, ivSize = intVals.size(), intv[] = + int count = 0, diff = 0, iv = 0, ivSize = shiftFrom.size(); + int[] intv = { 0, 0 }; while (iv < ivSize) { - intv = (int[]) intVals.elementAt(iv++); + intv = shiftFrom.get(iv++); diff = intv[1] - intv[0]; if (diff >= 0) { @@ -487,69 +520,68 @@ public class MapList * find series of intervals mapping from start-end in the From map. * * @param start - * position in to map + * position mapped 'to' * @param end - * position in to map - * @return series of ranges in from map + * position mapped 'to' + * @return series of [start, end] ranges in sequence mapped 'from' */ public int[] locateInFrom(int start, int end) { // inefficient implementation int fromStart[] = shiftTo(start); - int fromEnd[] = shiftTo(end); // needs to be inclusive of end of symbol - // position - if (fromStart == null || fromEnd == null) - return null; - int iv[] = getIntervals(fromShifts, fromStart, fromEnd, fromRatio); - return iv; + // needs to be inclusive of end of symbol position + int fromEnd[] = shiftTo(end); + + return getIntervals(fromShifts, fromStart, fromEnd, fromRatio); } /** * find series of intervals mapping from start-end in the to map. * * @param start - * position in from map + * position mapped 'from' * @param end - * position in from map - * @return series of ranges in to map + * position mapped 'from' + * @return series of [start, end] ranges in sequence mapped 'to' */ public int[] locateInTo(int start, int end) { - // inefficient implementation int toStart[] = shiftFrom(start); int toEnd[] = shiftFrom(end); - if (toStart == null || toEnd == null) - return null; - int iv[] = getIntervals(toShifts, toStart, toEnd, toRatio); - return iv; + return getIntervals(toShifts, toStart, toEnd, toRatio); } /** * like shift - except returns the intervals in the given vector of shifts * which were spanned in traversing fromStart to fromEnd * - * @param fromShifts2 + * @param shiftFrom * @param fromStart * @param fromEnd * @param fromRatio2 * @return series of from,to intervals from from first position of starting * region to final position of ending region inclusive */ - private int[] getIntervals(Vector fromShifts2, int[] fromStart, + protected static int[] getIntervals(List shiftFrom, + int[] fromStart, int[] fromEnd, int fromRatio2) { + if (fromStart == null || fromEnd == null) + { + return null; + } int startpos, endpos; startpos = fromStart[0]; // first position in fromStart endpos = fromEnd[0]; // last position in fromEnd int endindx = (fromRatio2 - 1); // additional positions to get to last // position from endpos - int intv = 0, intvSize = fromShifts2.size(); + int intv = 0, intvSize = shiftFrom.size(); int iv[], i = 0, fs = -1, fe_s = -1, fe = -1; // containing intervals // search intervals to locate ones containing startpos and count endindx // positions on from endpos while (intv < intvSize && (fs == -1 || fe == -1)) { - iv = (int[]) fromShifts2.elementAt(intv++); + iv = shiftFrom.get(intv++); if (fe_s > -1) { endpos = iv[0]; // start counting from beginning of interval @@ -612,39 +644,45 @@ public class MapList i++; } if (fs == fe && fe == -1) + { return null; - Vector ranges = new Vector(); + } + List ranges = new ArrayList(); if (fs <= fe) { intv = fs; i = fs; // truncate initial interval - iv = (int[]) fromShifts2.elementAt(intv++); + iv = shiftFrom.get(intv++); iv = new int[] { iv[0], iv[1] };// clone if (i == fs) + { iv[0] = startpos; + } while (i != fe) { - ranges.addElement(iv); // add initial range - iv = (int[]) fromShifts2.elementAt(intv++); // get next interval + ranges.add(iv); // add initial range + iv = shiftFrom.get(intv++); // get next interval iv = new int[] { iv[0], iv[1] };// clone i++; } if (i == fe) + { iv[1] = endpos; - ranges.addElement(iv); // add only - or final range + } + ranges.add(iv); // add only - or final range } else { // walk from end of interval. - i = fromShifts2.size() - 1; + i = shiftFrom.size() - 1; while (i > fs) { i--; } - iv = (int[]) fromShifts2.elementAt(i); + iv = shiftFrom.get(i); iv = new int[] { iv[1], iv[0] };// reverse and clone // truncate initial interval @@ -654,8 +692,8 @@ public class MapList } while (--i != fe) { // fix apparent logic bug when fe==-1 - ranges.addElement(iv); // add (truncated) reversed interval - iv = (int[]) fromShifts2.elementAt(i); + ranges.add(iv); // add (truncated) reversed interval + iv = shiftFrom.get(i); iv = new int[] { iv[1], iv[0] }; // reverse and clone } @@ -664,7 +702,7 @@ public class MapList // interval is already reversed iv[1] = endpos; } - ranges.addElement(iv); // add only - or final range + ranges.add(iv); // add only - or final range } // create array of start end intervals. int[] range = null; @@ -676,10 +714,10 @@ public class MapList i = 0; while (intv < intvSize) { - iv = (int[]) ranges.elementAt(intv); + iv = ranges.get(intv); range[i++] = iv[0]; range[i++] = iv[1]; - ranges.setElementAt(null, intv++); // remove + ranges.set(intv++, null); // remove } } return range; @@ -694,6 +732,7 @@ public class MapList */ public int getToPosition(int mpos) { + // TODO not used - remove?? int[] mp = shiftTo(mpos); if (mp != null) { @@ -730,6 +769,7 @@ public class MapList */ public int getMappedPosition(int pos) { + // TODO not used - remove?? int[] mp = shiftFrom(pos); if (mp != null) { @@ -740,6 +780,7 @@ public class MapList public int[] getMappedWord(int pos) { + // TODO not used - remove?? int[] mp = shiftFrom(pos); if (mp != null) { @@ -750,223 +791,6 @@ public class MapList } /** - * test routine. not incremental. - * - * @param ml - * @param fromS - * @param fromE - */ - public static void testMap(MapList ml, int fromS, int fromE) - { - for (int from = 1; from <= 25; from++) - { - int[] too = ml.shiftFrom(from); - System.out.print("ShiftFrom(" + from + ")=="); - if (too == null) - { - System.out.print("NaN\n"); - } - else - { - System.out.print(too[0] + " % " + too[1] + " (" + too[2] + ")"); - System.out.print("\t+--+\t"); - int[] toofrom = ml.shiftTo(too[0]); - if (toofrom != null) - { - if (toofrom[0] != from) - { - System.err.println("Mapping not reflexive:" + from + " " - + too[0] + "->" + toofrom[0]); - } - System.out.println("ShiftTo(" + too[0] + ")==" + toofrom[0] - + " % " + toofrom[1] + " (" + toofrom[2] + ")"); - } - else - { - System.out.println("ShiftTo(" + too[0] + ")==" - + "NaN! - not Bijective Mapping!"); - } - } - } - int mmap[][] = ml.makeFromMap(); - System.out.println("FromMap : (" + mmap[0][0] + " " + mmap[0][1] + " " - + mmap[0][2] + " " + mmap[0][3] + " "); - for (int i = 1; i <= mmap[1].length; i++) - { - if (mmap[1][i - 1] == -1) - { - System.out.print(i + "=XXX"); - - } - else - { - System.out.print(i + "=" + (mmap[0][2] + mmap[1][i - 1])); - } - if (i % 20 == 0) - { - System.out.print("\n"); - } - else - { - System.out.print(","); - } - } - // test range function - System.out.print("\nTest locateInFrom\n"); - { - int f = mmap[0][2], t = mmap[0][3]; - while (f <= t) - { - System.out.println("Range " + f + " to " + t); - int rng[] = ml.locateInFrom(f, t); - if (rng != null) - { - for (int i = 0; i < rng.length; i++) - { - System.out.print(rng[i] + ((i % 2 == 0) ? "," : ";")); - } - } - else - { - System.out.println("No range!"); - } - System.out.print("\nReversed\n"); - rng = ml.locateInFrom(t, f); - if (rng != null) - { - for (int i = 0; i < rng.length; i++) - { - System.out.print(rng[i] + ((i % 2 == 0) ? "," : ";")); - } - } - else - { - System.out.println("No range!"); - } - System.out.print("\n"); - f++; - t--; - } - } - System.out.print("\n"); - mmap = ml.makeToMap(); - System.out.println("ToMap : (" + mmap[0][0] + " " + mmap[0][1] + " " - + mmap[0][2] + " " + mmap[0][3] + " "); - for (int i = 1; i <= mmap[1].length; i++) - { - if (mmap[1][i - 1] == -1) - { - System.out.print(i + "=XXX"); - - } - else - { - System.out.print(i + "=" + (mmap[0][2] + mmap[1][i - 1])); - } - if (i % 20 == 0) - { - System.out.print("\n"); - } - else - { - System.out.print(","); - } - } - System.out.print("\n"); - // test range function - System.out.print("\nTest locateInTo\n"); - { - int f = mmap[0][2], t = mmap[0][3]; - while (f <= t) - { - System.out.println("Range " + f + " to " + t); - int rng[] = ml.locateInTo(f, t); - if (rng != null) - { - for (int i = 0; i < rng.length; i++) - { - System.out.print(rng[i] + ((i % 2 == 0) ? "," : ";")); - } - } - else - { - System.out.println("No range!"); - } - System.out.print("\nReversed\n"); - rng = ml.locateInTo(t, f); - if (rng != null) - { - for (int i = 0; i < rng.length; i++) - { - System.out.print(rng[i] + ((i % 2 == 0) ? "," : ";")); - } - } - else - { - System.out.println("No range!"); - } - f++; - t--; - System.out.print("\n"); - } - } - - } - - public static void main(String argv[]) - { - MapList ml = new MapList(new int[] - { 1, 5, 10, 15, 25, 20 }, new int[] - { 51, 1 }, 1, 3); - MapList ml1 = new MapList(new int[] - { 1, 3, 17, 4 }, new int[] - { 51, 1 }, 1, 3); - MapList ml2 = new MapList(new int[] - { 1, 60 }, new int[] - { 1, 20 }, 3, 1); - // test internal consistency - int to[] = new int[51]; - MapList.testMap(ml, 1, 60); - MapList mldna = new MapList(new int[] - { 2, 2, 6, 8, 12, 16 }, new int[] - { 1, 3 }, 3, 1); - int[] frm = mldna.locateInFrom(1, 1); - testLocateFrom(mldna, 1, 1, new int[] - { 2, 2, 6, 7 }); - MapList.testMap(mldna, 1, 3); - /* - * for (int from=1; from<=51; from++) { int[] too=ml.shiftTo(from); int[] - * toofrom=ml.shiftFrom(too[0]); - * System.out.println("ShiftFrom("+from+")=="+too[0]+" % - * "+too[1]+"\t+-+\tShiftTo("+too[0]+")=="+toofrom[0]+" % "+toofrom[1]); } - */ - System.out.print("Success?\n"); // if we get here - something must be - // working! - } - - private static void testLocateFrom(MapList mldna, int i, int j, int[] ks) - { - int[] frm = mldna.locateInFrom(i, j); - if (frm == ks || java.util.Arrays.equals(frm, ks)) - { - System.out.println("Success test locate from " + i + " to " + j); - } - else - { - System.err.println("Failed test locate from " + i + " to " + j); - for (int c = 0; c < frm.length; c++) - { - System.err.print(frm[c] + ((c % 2 == 0) ? "," : ";")); - } - System.err.println("Expected"); - for (int c = 0; c < ks.length; c++) - { - System.err.print(ks[c] + ((c % 2 == 0) ? "," : ";")); - } - } - } - - /** * * @return a MapList whose From range is this maplist's To Range, and vice * versa @@ -987,6 +811,7 @@ public class MapList */ public boolean containsEither(boolean local, MapList map) { + // TODO not used - remove? if (local) { return ((getFromLowest() >= map.getFromLowest() && getFromHighest() <= map diff --git a/src/jalview/util/MappingUtils.java b/src/jalview/util/MappingUtils.java new file mode 100644 index 0000000..6dfebfe --- /dev/null +++ b/src/jalview/util/MappingUtils.java @@ -0,0 +1,481 @@ +package jalview.util; + +import jalview.analysis.AlignmentSorter; +import jalview.api.AlignViewportI; +import jalview.commands.CommandI; +import jalview.commands.EditCommand; +import jalview.commands.EditCommand.Action; +import jalview.commands.EditCommand.Edit; +import jalview.commands.OrderCommand; +import jalview.datamodel.AlignedCodonFrame; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.AlignmentOrder; +import jalview.datamodel.ColumnSelection; +import jalview.datamodel.SearchResults; +import jalview.datamodel.SearchResults.Match; +import jalview.datamodel.Sequence; +import jalview.datamodel.SequenceGroup; +import jalview.datamodel.SequenceI; +import jalview.gui.AlignViewport; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Helper methods for manipulations involving sequence mappings. + * + * @author gmcarstairs + * + */ +public final class MappingUtils +{ + + /** + * Helper method to map a CUT or PASTE command. + * + * @param edit + * the original command + * @param undo + * if true, the command is to be undone + * @param targetSeqs + * the mapped sequences to apply the mapped command to + * @param result + * the mapped EditCommand to add to + * @param mappings + */ + protected static void mapCutOrPaste(Edit edit, boolean undo, + List targetSeqs, EditCommand result, + Set mappings) + { + Action action = edit.getAction(); + if (undo) + { + action = action.getUndoAction(); + } + // TODO write this + System.err.println("MappingUtils.mapCutOrPaste not yet implemented"); + } + + /** + * Returns a new EditCommand representing the given command as mapped to the + * given sequences. If there is no mapping, returns null. + * + * @param command + * @param undo + * @param mapTo + * @param gapChar + * @param mappings + * @return + */ + public static EditCommand mapEditCommand(EditCommand command, + boolean undo, final AlignmentI mapTo, char gapChar, + Set mappings) + { + /* + * For now, only support mapping from protein edits to cDna + */ + if (!mapTo.isNucleotide()) + { + return null; + } + + /* + * 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 targetCopies = new HashMap(); + for (SequenceI seq : mapTo.getSequences()) + { + SequenceI ds = seq.getDatasetSequence(); + if (ds != null) + { + final SequenceI copy = new Sequence("", new String( + seq.getSequence())); + copy.setDatasetSequence(ds); + targetCopies.put(ds, copy); + } + } + + /* + * Compute 'source' sequences as they were before applying edits: + */ + Map originalSequences = command.priorState(undo); + + EditCommand result = new EditCommand(); + Iterator edits = command.getEditIterator(!undo); + while (edits.hasNext()) + { + Edit edit = edits.next(); + if (edit.getAction() == Action.CUT + || edit.getAction() == Action.PASTE) + { + mapCutOrPaste(edit, undo, mapTo.getSequences(), result, mappings); + } + else if (edit.getAction() == Action.INSERT_GAP + || edit.getAction() == Action.DELETE_GAP) + { + mapInsertOrDelete(edit, undo, originalSequences, + mapTo.getSequences(), targetCopies, gapChar, result, + mappings); + } + } + return result.getSize() > 0 ? result : null; + } + + /** + * Helper method to map an edit command to insert or delete gaps. + * + * @param edit + * the original command + * @param undo + * if true, the action is to undo the command + * @param originalSequences + * the sequences the command acted on + * @param targetSeqs + * @param targetCopies + * @param gapChar + * @param result + * the new EditCommand to add mapped commands to + * @param mappings + */ + protected static void mapInsertOrDelete(Edit edit, boolean undo, + Map originalSequences, + final List targetSeqs, + Map targetCopies, char gapChar, + EditCommand result, Set mappings) + { + Action action = edit.getAction(); + + /* + * Invert sense of action if an Undo. + */ + if (undo) + { + action = action.getUndoAction(); + } + 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, mappings); + + 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 = result.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))); + } + } + } + + /** + * Returns a SearchResults object describing the mapped region corresponding + * to the specified sequence position. + * + * @param seq + * @param index + * @param seqmappings + * @return + */ + public static SearchResults buildSearchResults(SequenceI seq, int index, + Set seqmappings) + { + SearchResults results; + results = new SearchResults(); + if (index >= seq.getStart() && index <= seq.getEnd()) + { + for (AlignedCodonFrame acf : seqmappings) + { + acf.markMappedRegion(seq, index, results); + } + } + return results; + } + + /** + * Returns a (possibly empty) SequenceGroup containing any sequences in the + * mapped viewport corresponding to the given group in the source viewport. + * + * @param sg + * @param mapFrom + * @param mapTo + * @return + */ + public static SequenceGroup mapSequenceGroup(SequenceGroup sg, + AlignViewportI mapFrom, AlignViewportI mapTo) + { + /* + * Note the SequenceGroup holds aligned sequences, the mappings hold dataset + * sequences. + */ + boolean targetIsNucleotide = mapTo.isNucleotide(); + AlignViewportI protein = targetIsNucleotide ? mapFrom : mapTo; + Set codonFrames = protein.getAlignment() + .getCodonFrames(); + + /* + * Copy group name, colours, but not sequences + */ + SequenceGroup mappedGroup = new SequenceGroup(sg); + mappedGroup.clear(); + // TODO set width of mapped group + + for (SequenceI selected : sg.getSequences()) + { + for (AlignedCodonFrame acf : codonFrames) + { + SequenceI mappedSequence = targetIsNucleotide ? acf + .getDnaForAaSeq(selected) : acf.getAaForDnaSeq(selected); + if (mappedSequence != null) + { + for (SequenceI seq : mapTo.getAlignment().getSequences()) + { + if (seq.getDatasetSequence() == mappedSequence) + { + mappedGroup.addSequence(seq, false); + break; + } + } + } + } + } + return mappedGroup; + } + + /** + * Returns an OrderCommand equivalent to the given one, but acting on mapped + * sequences as described by the mappings, or null if no mapping can be made. + * + * @param command + * the original order command + * @param undo + * if true, the action is to undo the sort + * @param mapTo + * the alignment we are mapping to + * @param mappings + * the mappings available + * @return + */ + public static CommandI mapOrderCommand(OrderCommand command, + boolean undo, AlignmentI mapTo, Set mappings) + { + SequenceI[] sortOrder = command.getSequenceOrder(undo); + List mappedOrder = new ArrayList(); + int j = 0; + for (SequenceI seq : sortOrder) + { + for (AlignedCodonFrame acf : mappings) + { + /* + * Try protein-to-Dna, failing that try dna-to-protein + */ + SequenceI mappedSeq = acf.getDnaForAaSeq(seq); + if (mappedSeq == null) + { + mappedSeq = acf.getAaForDnaSeq(seq); + } + if (mappedSeq != null) + { + for (SequenceI seq2 : mapTo.getSequences()) + { + if (seq2.getDatasetSequence() == mappedSeq) + { + mappedOrder.add(seq2); + j++; + break; + } + } + } + } + } + + /* + * Return null if no mappings made. + */ + if (j == 0) + { + return null; + } + + /* + * Add any unmapped sequences on the end of the sort in their original + * ordering. + */ + if (j < mapTo.getHeight()) + { + for (SequenceI seq : mapTo.getSequences()) + { + if (!mappedOrder.contains(seq)) + { + mappedOrder.add(seq); + } + } + } + + /* + * Have to align the sequences before constructing the OrderCommand - which + * then realigns them?!? + */ + final SequenceI[] mappedOrderArray = mappedOrder + .toArray(new SequenceI[mappedOrder.size()]); + SequenceI[] oldOrder = mapTo.getSequencesArray(); + AlignmentSorter.sortBy(mapTo, new AlignmentOrder(mappedOrderArray)); + final OrderCommand result = new OrderCommand(command.getDescription(), + oldOrder, mapTo); + return result; + } + + /** + * Returns a ColumnSelection in the 'mapTo' view which corresponds to the + * given selection in the 'mapFrom' view. We assume one is nucleotide, the + * other is protein (and holds the mappings from codons to protein residues). + * + * @param colsel + * @param mapFrom + * @param mapTo + * @return + */ + public static ColumnSelection mapColumnSelection(ColumnSelection colsel, + AlignViewportI mapFrom, AlignViewport mapTo) + { + boolean targetIsNucleotide = mapTo.isNucleotide(); + AlignViewportI protein = targetIsNucleotide ? mapFrom : mapTo; + Set codonFrames = protein.getAlignment() + .getCodonFrames(); + ColumnSelection mappedColumns = new ColumnSelection(); + char fromGapChar = mapFrom.getAlignment().getGapCharacter(); + + for (Object obj : colsel.getSelected()) + { + int col = ((Integer) obj).intValue(); + int mappedToMin = Integer.MAX_VALUE; + int mappedToMax = Integer.MIN_VALUE; + + /* + * For each sequence in the 'from' alignment + */ + for (SequenceI fromSeq : mapFrom.getAlignment().getSequences()) + { + /* + * Ignore gaps (unmapped anyway) + */ + if (fromSeq.getCharAt(col) == fromGapChar) + { + continue; + } + + /* + * Get the residue position and find the mapped position. + */ + int residuePos = fromSeq.findPosition(col); + SearchResults sr = buildSearchResults(fromSeq, residuePos, + codonFrames); + for (Match m : sr.getResults()) + { + int mappedStartResidue = m.getStart(); + int mappedEndResidue = m.getEnd(); + SequenceI mappedSeq = m.getSequence(); + + /* + * Locate the aligned sequence whose dataset is mappedSeq. TODO a + * datamodel that can do this efficiently. + */ + for (SequenceI toSeq : mapTo.getAlignment().getSequences()) + { + if (toSeq.getDatasetSequence() == mappedSeq) + { + int mappedStartCol = toSeq.findIndex(mappedStartResidue); + int mappedEndCol = toSeq.findIndex(mappedEndResidue); + mappedToMin = Math.min(mappedToMin, mappedStartCol); + mappedToMax = Math.max(mappedToMax, mappedEndCol); + // System.out.println(fromSeq.getName() + " mapped to cols " + // + mappedStartCol + ":" + mappedEndCol); + break; + // TODO remove break if we ever want to map one to many sequences + } + } + } + } + /* + * Add mapped columns to mapped selection (converting base 1 to base 0) + */ + for (int i = mappedToMin; i <= mappedToMax; i++) + { + mappedColumns.addElement(i - 1); + } + } + return mappedColumns; + } + +} diff --git a/src/jalview/util/ReverseListIterator.java b/src/jalview/util/ReverseListIterator.java new file mode 100644 index 0000000..69a7345 --- /dev/null +++ b/src/jalview/util/ReverseListIterator.java @@ -0,0 +1,42 @@ +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 + */ +public class ReverseListIterator implements Iterator +{ + + private ListIterator iterator; + + public ReverseListIterator(List 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(); + } + +} diff --git a/src/jalview/util/ShiftList.java b/src/jalview/util/ShiftList.java index 2b3a8a4..3b8c68f 100644 --- a/src/jalview/util/ShiftList.java +++ b/src/jalview/util/ShiftList.java @@ -20,7 +20,8 @@ */ package jalview.util; -import java.util.*; +import java.util.ArrayList; +import java.util.List; /** * ShiftList Simple way of mapping a linear series to a new linear range with @@ -29,11 +30,11 @@ import java.util.*; */ public class ShiftList { - public Vector shifts; + private List shifts; public ShiftList() { - shifts = new Vector(); + shifts = new ArrayList(); } /** @@ -48,15 +49,14 @@ public class ShiftList { int sidx = 0; int[] rshift = null; - while (sidx < shifts.size() - && (rshift = (int[]) shifts.elementAt(sidx))[0] < pos) + while (sidx < shifts.size() && (rshift = shifts.get(sidx))[0] < pos) { sidx++; } if (sidx == shifts.size()) { - shifts.insertElementAt(new int[] - { pos, shift }, sidx); + shifts.add(sidx, new int[] + { pos, shift }); } else { @@ -81,7 +81,7 @@ public class ShiftList int sidx = 0; int rshift[]; while (sidx < shifts.size() - && (rshift = ((int[]) shifts.elementAt(sidx++)))[0] <= pos) + && (rshift = (shifts.get(sidx++)))[0] <= pos) { shifted += rshift[1]; } @@ -93,7 +93,7 @@ public class ShiftList */ public void clear() { - shifts.removeAllElements(); + shifts.clear(); } /** @@ -108,10 +108,10 @@ public class ShiftList { for (int i = 0, j = shifts.size(); i < j; i++) { - int[] sh = (int[]) shifts.elementAt(i); + int[] sh = shifts.get(i); if (sh != null) { - inverse.shifts.addElement(new int[] + inverse.shifts.add(new int[] { sh[0], -sh[1] }); } } @@ -120,8 +120,8 @@ public class ShiftList } /** - * parse a 1d map of position 1 getShifts() + { + return shifts; + } } diff --git a/src/jalview/util/StringUtils.java b/src/jalview/util/StringUtils.java new file mode 100644 index 0000000..0544864 --- /dev/null +++ b/src/jalview/util/StringUtils.java @@ -0,0 +1,106 @@ +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; + } + + /** + * Returns the last part of 'input' after the last occurrence of 'token'. For + * example to extract only the filename from a full path or URL. + * + * @param input + * @param token + * a delimiter which must be in regular expression format + * @return + */ + public static String getLastToken(String input, String token) + { + if (input == null) + { + return null; + } + if (token == null) + { + return input; + } + String[] st = input.split(token); + return st[st.length - 1]; + } +} diff --git a/src/jalview/viewmodel/AlignmentViewport.java b/src/jalview/viewmodel/AlignmentViewport.java index 08b31fa..364222e 100644 --- a/src/jalview/viewmodel/AlignmentViewport.java +++ b/src/jalview/viewmodel/AlignmentViewport.java @@ -29,6 +29,7 @@ import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.AlignmentView; import jalview.datamodel.Annotation; +import jalview.datamodel.CigarArray; import jalview.datamodel.ColumnSelection; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceCollectionI; @@ -45,6 +46,7 @@ import jalview.workers.StrucConsensusThread; import java.awt.Color; import java.util.ArrayList; import java.util.BitSet; +import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.Map; @@ -59,6 +61,12 @@ import java.util.Vector; */ public abstract class AlignmentViewport implements AlignViewportI { + /* + * A viewport that hosts the cDna view of this (protein), or vice versa (if + * set). + */ + AlignViewportI codingComplement = null; + /** * alignment displayed in the viewport. Please use get/setter */ @@ -813,7 +821,7 @@ public abstract class AlignmentViewport implements AlignViewportI protected boolean showConsensus = true; - Hashtable sequenceColours; + private Map sequenceColours = new HashMap(); /** * Property change listener for changes in alignment @@ -1050,9 +1058,6 @@ public abstract class AlignmentViewport implements AlignViewportI } @Override - public abstract void sendSelection(); - - @Override public void invertColumnSelection() { colSel.invertColumnSelection(0, alignment.getWidth()); @@ -1106,10 +1111,10 @@ public abstract class AlignmentViewport implements AlignViewportI @Override - public jalview.datamodel.CigarArray getViewAsCigars( + public CigarArray getViewAsCigars( boolean selectedRegionOnly) { - return new jalview.datamodel.CigarArray(alignment, colSel, + return new CigarArray(alignment, colSel, (selectedRegionOnly ? selectionGroup : null)); } @@ -1572,11 +1577,13 @@ public abstract class AlignmentViewport implements AlignViewportI this.displayReferenceSeq = displayReferenceSeq; } + @Override public boolean isColourByReferenceSeq() { return alignment.hasSeqrep() && colourByReferenceSeq; } + @Override public void setColourByReferenceSeq(boolean colourByReferenceSeq) { this.colourByReferenceSeq = colourByReferenceSeq; @@ -1585,26 +1592,13 @@ public abstract class AlignmentViewport implements AlignViewportI @Override public Color getSequenceColour(SequenceI seq) { - Color sqc = Color.white; - if (sequenceColours != null) - { - sqc = (Color) sequenceColours.get(seq); - if (sqc == null) - { - sqc = Color.white; - } - } - return sqc; + Color sqc = sequenceColours.get(seq); + return (sqc == null ? Color.white : sqc); } @Override public void setSequenceColour(SequenceI seq, Color col) { - if (sequenceColours == null) - { - sequenceColours = new Hashtable(); - } - if (col == null) { sequenceColours.remove(seq); @@ -1618,10 +1612,6 @@ public abstract class AlignmentViewport implements AlignViewportI @Override public void updateSequenceIdColours() { - if (sequenceColours == null) - { - sequenceColours = new Hashtable(); - } for (SequenceGroup sg : alignment.getGroups()) { if (sg.idColour != null) @@ -1637,9 +1627,43 @@ public abstract class AlignmentViewport implements AlignViewportI @Override public void clearSequenceColours() { - sequenceColours = null; + sequenceColours.clear(); }; + @Override + public AlignViewportI getCodingComplement() + { + return this.codingComplement; + } + + /** + * Set this as the (cDna/protein) complement of the given viewport. Also + * ensures the reverse relationship is set on the given viewport. + */ + @Override + public void setCodingComplement(AlignViewportI av) + { + if (this == av) + { + System.err.println("Ignoring recursive setCodingComplement request"); + } + else + { + this.codingComplement = av; + // avoid infinite recursion! + if (av.getCodingComplement() != this) + { + av.setCodingComplement(this); + } + } + } + + @Override + public boolean isNucleotide() + { + return getAlignment() == null ? false : getAlignment().isNucleotide(); + } + FeaturesDisplayedI featuresDisplayed = null; @Override @@ -1724,5 +1748,4 @@ public abstract class AlignmentViewport implements AlignViewportI { this.rightAlignIds = rightAlignIds; } - } diff --git a/src/jalview/ws/AWSThread.java b/src/jalview/ws/AWSThread.java index d3682d4..0945b2a 100644 --- a/src/jalview/ws/AWSThread.java +++ b/src/jalview/ws/AWSThread.java @@ -28,8 +28,11 @@ import jalview.datamodel.AlignmentView; import jalview.datamodel.SequenceI; import jalview.gui.AlignFrame; import jalview.gui.WebserviceInfo; -import jalview.viewmodel.seqfeatures.FeatureRendererSettings; import jalview.util.MessageManager; +import jalview.viewmodel.seqfeatures.FeatureRendererSettings; + +import java.util.LinkedHashSet; +import java.util.Set; public abstract class AWSThread extends Thread { @@ -57,7 +60,7 @@ public abstract class AWSThread extends Thread /** * dataset sequence relationships to be propagated onto new results */ - protected AlignedCodonFrame[] codonframe = null; + protected Set codonframe = null; /** * are there jobs still running in this thread. @@ -124,8 +127,9 @@ public abstract class AWSThread extends Thread } catch (Exception ex) { // Deal with Transaction exceptions - wsInfo.appendProgressText(jobs[j].jobnum, - MessageManager.formatMessage("info.server_exception", new String[]{WebServiceName,ex.getMessage()})); + wsInfo.appendProgressText(jobs[j].jobnum, MessageManager + .formatMessage("info.server_exception", new Object[] + { WebServiceName, ex.getMessage() })); // always output the exception's stack trace to the log Cache.log.warn(WebServiceName + " job(" + jobs[j].jobnum + ") Server exception."); @@ -189,7 +193,7 @@ public abstract class AWSThread extends Thread { Cache.log .debug("WebServiceJob poll loop finished with no jobs created."); - wsInfo.setStatus(wsInfo.STATE_STOPPED_ERROR); + wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR); wsInfo.appendProgressText(MessageManager.getString("info.no_jobs_ran")); wsInfo.setFinishedNoResults(); } @@ -295,13 +299,12 @@ public abstract class AWSThread extends Thread SequenceI[] alignment = al.getSequencesArray(); for (int sq = 0; sq < alignment.length; sq++) { - for (int i = 0; i < codonframe.length; i++) + for (AlignedCodonFrame acf : codonframe) { - if (codonframe[i] != null - && codonframe[i].involvesSequence(alignment[sq])) + final SequenceI seq = alignment[sq]; + if (acf != null && acf.involvesSequence(seq)) { - al.addCodonFrame(codonframe[i]); - codonframe[i] = null; + al.addCodonFrame(acf); break; } } @@ -369,12 +372,12 @@ public abstract class AWSThread extends Thread WsUrl = wsurl2; if (alframe != null) { - AlignedCodonFrame[] cf = alframe.getViewport().getAlignment() + Set cf = alframe.getViewport().getAlignment() .getCodonFrames(); if (cf != null) { - codonframe = new AlignedCodonFrame[cf.length]; - System.arraycopy(cf, 0, codonframe, 0, cf.length); + codonframe = new LinkedHashSet(); + codonframe.addAll(cf); } } } diff --git a/test/jalview/analysis/AlignmentUtilsTests.java b/test/jalview/analysis/AlignmentUtilsTests.java index 18b4252..d1300fe 100644 --- a/test/jalview/analysis/AlignmentUtilsTests.java +++ b/test/jalview/analysis/AlignmentUtilsTests.java @@ -20,19 +20,63 @@ */ package jalview.analysis; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; - -import org.junit.Test; - +import jalview.analysis.AlignmentUtils.MappingResult; +import jalview.datamodel.AlignedCodonFrame; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentI; +import jalview.datamodel.Mapping; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceI; import jalview.io.AppletFormatAdapter; +import jalview.io.FormatAdapter; +import jalview.util.MapList; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.junit.Test; public class AlignmentUtilsTests { + // @formatter:off + private static final String TEST_DATA = + "# STOCKHOLM 1.0\n" + + "#=GS D.melanogaster.1 AC AY119185.1/838-902\n" + + "#=GS D.melanogaster.2 AC AC092237.1/57223-57161\n" + + "#=GS D.melanogaster.3 AC AY060611.1/560-627\n" + + "D.melanogaster.1 G.AGCC.CU...AUGAUCGA\n" + + "#=GR D.melanogaster.1 SS ................((((\n" + + "D.melanogaster.2 C.AUUCAACU.UAUGAGGAU\n" + + "#=GR D.melanogaster.2 SS ................((((\n" + + "D.melanogaster.3 G.UGGCGCU..UAUGACGCA\n" + + "#=GR D.melanogaster.3 SS (.(((...(....(((((((\n" + + "//"; + + private static final String AA_SEQS_1 = + ">Seq1Name\n" + + "K-QY--L\n" + + ">Seq2Name\n" + + "-R-FP-W-\n"; + + private static final String CDNA_SEQS_1 = + ">Seq1Name\n" + + "AC-GG--CUC-CAA-CT\n" + + ">Seq2Name\n" + + "-CG-TTA--ACG---AAGT\n"; + + private static final String CDNA_SEQS_2 = + ">Seq1Name\n" + + "GCTCGUCGTACT\n" + + ">Seq2Name\n" + + "GGGTCAGGCAGT\n"; + // @formatter:on + public static Sequence ts=new Sequence("short","ASDASDASDASDASDASDASDASDASDASDASDASDASD"); + @Test public void testExpandFlanks() { @@ -55,6 +99,386 @@ public class AlignmentUtilsTests assertTrue("Flanking sequence not the same as original dataset sequence.\n"+ung+"\n"+sq.getDatasetSequence().getSequenceAsString(),ung.equalsIgnoreCase(sq.getDatasetSequence().getSequenceAsString())); } } + } } + + /** + * Test method that returns a map of lists of sequences by sequence name. + * + * @throws IOException + */ + @Test + public void testGetSequencesByName() throws IOException + { + final String data = ">Seq1Name\nKQYL\n" + ">Seq2Name\nRFPW\n" + + ">Seq1Name\nABCD\n"; + AlignmentI al = loadAlignment(data, "FASTA"); + Map> map = AlignmentUtils + .getSequencesByName(al); + assertEquals(2, map.keySet().size()); + assertEquals(2, map.get("Seq1Name").size()); + assertEquals("KQYL", map.get("Seq1Name").get(0).getSequenceAsString()); + assertEquals("ABCD", map.get("Seq1Name").get(1).getSequenceAsString()); + assertEquals(1, map.get("Seq2Name").size()); + assertEquals("RFPW", map.get("Seq2Name").get(0).getSequenceAsString()); + } + /** + * Helper method to load an alignment and ensure dataset sequences are set up. + * + * @param data + * @param format TODO + * @return + * @throws IOException + */ + protected AlignmentI loadAlignment(final String data, String format) throws IOException + { + Alignment a = new FormatAdapter().readFile(data, + AppletFormatAdapter.PASTE, format); + a.setDataset(null); + return a; + } + /** + * Test mapping of protein to cDNA. + * + * @throws IOException + */ + @Test + public void testMapProteinToCdna() throws IOException + { + // protein: Human + Mouse, 3 residues + AlignmentI protein = loadAlignment( + ">Human\nKQY\n>Mouse\nAFP\n>Worm\nRST\n", + "FASTA"); + // cDNA: Mouse, Human, Mouse, 9 bases + // @formatter:off + String dnaData = + ">Mouse\nGAAATCCAG\n" + + ">Human\nTTCGATTAC\n" + + ">Mouse\nGTCGTTTGC\n" + + ">Mouse\nGTCGTTTGCgac\n" + // not mapped - wrong length + ">Fly\nGTCGTTTGC\n"; // not mapped - no name match + // @formatter:on + AlignmentI cdna1 = loadAlignment( + dnaData, + "FASTA"); + MappingResult mapped = AlignmentUtils.mapProteinToCdna(protein, cdna1); + assertEquals(mapped, MappingResult.Mapped); + + /* + * Check two mappings (one for Mouse, one for Human) + */ + assertEquals(2, protein.getCodonFrames().size()); + assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(0)).size()); + assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(1)).size()); + + /* + * Inspect mapping for Human protein + */ + AlignedCodonFrame humanMapping = protein.getCodonFrame( + protein.getSequenceAt(0)).get(0); + assertEquals(1, humanMapping.getdnaSeqs().length); + assertEquals(cdna1.getSequenceAt(1).getDatasetSequence(), + humanMapping.getdnaSeqs()[0]); + Mapping[] protMappings = humanMapping.getProtMappings(); + assertEquals(1, protMappings.length); + MapList mapList = protMappings[0].getMap(); + assertEquals(3, mapList.getFromRatio()); + assertEquals(1, mapList.getToRatio()); + assertTrue(Arrays.equals(new int[] + { 1, 9 }, mapList.getFromRanges())); + assertTrue(Arrays.equals(new int[] + { 1, 3 }, mapList.getToRanges())); + + /* + * Inspect mappings for Mouse protein + */ + AlignedCodonFrame mouseMapping1 = protein.getCodonFrame( + protein.getSequenceAt(1)).get(0); + assertEquals(2, mouseMapping1.getdnaSeqs().length); + assertEquals(cdna1.getSequenceAt(0).getDatasetSequence(), + mouseMapping1.getdnaSeqs()[0]); + assertEquals(cdna1.getSequenceAt(2).getDatasetSequence(), + mouseMapping1.getdnaSeqs()[1]); + protMappings = mouseMapping1.getProtMappings(); + assertEquals(2, protMappings.length); + for (int i = 0; i < 2; i++) + { + mapList = protMappings[i].getMap(); + assertEquals(3, mapList.getFromRatio()); + assertEquals(1, mapList.getToRatio()); + assertTrue(Arrays.equals(new int[] + { 1, 9 }, mapList.getFromRanges())); + assertTrue(Arrays.equals(new int[] + { 1, 3 }, mapList.getToRanges())); + } + } + + /** + * Test mapping of protein to cDNA which may include start and/or stop codons. + * + * @throws IOException + */ + @Test + public void testMapProteinToCdna_stopStartCodons() throws IOException + { + // protein: Human + Mouse, 3 residues + AlignmentI protein = loadAlignment( + ">Human\nKQY\n>Mouse\nAFP\n>Worm\nRST\n", "FASTA"); + // @formatter:off + String dnaData = + ">Mouse\natgGAAATCCAG\n" + // Mouse with start codon + ">Human\nTTCGATtactaa\n" + // Human with stop codon TAA + ">Mouse\nGTCGTTTGctaG\n" + // Mouse with stop codon TAG + ">Human\nGTCGTTTgctGa\n" + // Human with stop codon TGA + ">Mouse\nATGGTCGTTTGCtag\n"; // Mouse with start and stop codons + // @formatter:on + AlignmentI cdna1 = loadAlignment( + dnaData, + "FASTA"); + MappingResult mapped = AlignmentUtils.mapProteinToCdna(protein, cdna1); + assertEquals(mapped, MappingResult.Mapped); + + /* + * Check two mappings (one for Mouse, one for Human) + */ + assertEquals(2, protein.getCodonFrames().size()); + assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(0)).size()); + assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(1)).size()); + + /* + * Inspect mapping for Human protein - should map to 2nd and 4th cDNA seqs + */ + AlignedCodonFrame humanMapping = protein.getCodonFrame( + protein.getSequenceAt(0)).get(0); + assertEquals(2, humanMapping.getdnaSeqs().length); + assertEquals(cdna1.getSequenceAt(1).getDatasetSequence(), + humanMapping.getdnaSeqs()[0]); + assertEquals(cdna1.getSequenceAt(3).getDatasetSequence(), + humanMapping.getdnaSeqs()[1]); + Mapping[] protMappings = humanMapping.getProtMappings(); + // two mappings, both to cDNA with stop codon + assertEquals(2, protMappings.length); + MapList mapList = protMappings[0].getMap(); + assertEquals(3, mapList.getFromRatio()); + assertEquals(1, mapList.getToRatio()); + assertTrue(Arrays.equals(new int[] + { 1, 9 }, mapList.getFromRanges())); + assertTrue(Arrays.equals(new int[] + { 1, 3 }, mapList.getToRanges())); + mapList = protMappings[1].getMap(); + assertEquals(3, mapList.getFromRatio()); + assertEquals(1, mapList.getToRatio()); + assertTrue(Arrays.equals(new int[] + { 1, 9 }, mapList.getFromRanges())); + assertTrue(Arrays.equals(new int[] + { 1, 3 }, mapList.getToRanges())); + + /* + * Inspect mapping for Mouse protein - should map to 1st/3rd/5th cDNA seqs + */ + AlignedCodonFrame mouseMapping = protein.getCodonFrame( + protein.getSequenceAt(1)).get(0); + assertEquals(3, mouseMapping.getdnaSeqs().length); + assertEquals(cdna1.getSequenceAt(0).getDatasetSequence(), + mouseMapping.getdnaSeqs()[0]); + assertEquals(cdna1.getSequenceAt(2).getDatasetSequence(), + mouseMapping.getdnaSeqs()[1]); + assertEquals(cdna1.getSequenceAt(4).getDatasetSequence(), + mouseMapping.getdnaSeqs()[2]); + + // three mappings + protMappings = mouseMapping.getProtMappings(); + assertEquals(3, protMappings.length); + + // first mapping to cDNA with start codon + mapList = protMappings[0].getMap(); + assertEquals(3, mapList.getFromRatio()); + assertEquals(1, mapList.getToRatio()); + assertTrue(Arrays.equals(new int[] + { 4, 12 }, mapList.getFromRanges())); + assertTrue(Arrays.equals(new int[] + { 1, 3 }, mapList.getToRanges())); + + // second mapping to cDNA with stop codon + mapList = protMappings[1].getMap(); + assertEquals(3, mapList.getFromRatio()); + assertEquals(1, mapList.getToRatio()); + assertTrue(Arrays.equals(new int[] + { 1, 9 }, mapList.getFromRanges())); + assertTrue(Arrays.equals(new int[] + { 1, 3 }, mapList.getToRanges())); + + // third mapping to cDNA with start and stop codon + mapList = protMappings[2].getMap(); + assertEquals(3, mapList.getFromRatio()); + assertEquals(1, mapList.getToRatio()); + assertTrue(Arrays.equals(new int[] + { 4, 12 }, mapList.getFromRanges())); + assertTrue(Arrays.equals(new int[] + { 1, 3 }, mapList.getToRanges())); + } + + /** + * Test for the alignSequenceAs method that takes two sequences and a mapping. + */ + @Test + public void testAlignSequenceAs_withMapping_noIntrons() + { + MapList map = new MapList(new int[] + { 1, 6 }, new int[] + { 1, 2 }, 3, 1); + + /* + * No existing gaps in dna: + */ + checkAlignSequenceAs("GGGAAA", "-A-L-", false, false, map, + "---GGG---AAA"); + + /* + * Now introduce gaps in dna but ignore them when realigning. + */ + checkAlignSequenceAs("-G-G-G-A-A-A-", "-A-L-", false, false, map, + "---GGG---AAA"); + + /* + * Now include gaps in dna when realigning. First retaining 'mapped' gaps + * only, i.e. those within the exon region. + */ + checkAlignSequenceAs("-G-G--G-A--A-A-", "-A-L-", true, false, map, + "---G-G--G---A--A-A"); + + /* + * Include all gaps in dna when realigning (within and without the exon + * region). The leading gap, and the gaps between codons, are subsumed by + * the protein alignment gap. + */ + checkAlignSequenceAs("-G-GG--AA-A-", "-A-L-", true, true, map, + "---G-GG---AA-A-"); + + /* + * Include only unmapped gaps in dna when realigning (outside the exon + * region). The leading gap, and the gaps between codons, are subsumed by + * the protein alignment gap. + */ + checkAlignSequenceAs("-G-GG--AA-A-", "-A-L-", false, true, map, + "---GGG---AAA-"); + } + + /** + * Test for the alignSequenceAs method that takes two sequences and a mapping. + */ + @Test + public void testAlignSequenceAs_withMapping_withIntrons() + { + /* + * Exons at codon 2 (AAA) and 4 (TTT) + */ + MapList map = new MapList(new int[] + { 4, 6, 10, 12 }, new int[] + { 1, 2 }, 3, 1); + + /* + * Simple case: no gaps in dna + */ + checkAlignSequenceAs("GGGAAACCCTTTGGG", "--A-L-", false, false, map, + "GGG---AAACCCTTTGGG"); + + /* + * Add gaps to dna - but ignore when realigning. + */ + checkAlignSequenceAs("-G-G-G--A--A---AC-CC-T-TT-GG-G-", "--A-L-", + false, false, map, "GGG---AAACCCTTTGGG"); + + /* + * Add gaps to dna - include within exons only when realigning. + */ + checkAlignSequenceAs("-G-G-G--A--A---A-C-CC-T-TT-GG-G-", "--A-L-", + true, false, map, "GGG---A--A---ACCCT-TTGGG"); + + /* + * Include gaps outside exons only when realigning. + */ + checkAlignSequenceAs("-G-G-G--A--A---A-C-CC-T-TT-GG-G-", "--A-L-", + false, true, map, "-G-G-GAAAC-CCTTT-GG-G-"); + + /* + * Include gaps following first intron if we are 'preserving mapped gaps' + */ + checkAlignSequenceAs("-G-G-G--A--A---A-C-CC-T-TT-GG-G-", "--A-L-", + true, true, map, "-G-G-G--A--A---A-C-CC-T-TT-GG-G-"); + + /* + * Include all gaps in dna when realigning. + */ + checkAlignSequenceAs("-G-G-G--A--A---A-C-CC-T-TT-GG-G-", "--A-L-", + true, true, map, "-G-G-G--A--A---A-C-CC-T-TT-GG-G-"); + } + + /** + * Test for the case where not all of the protein sequence is mapped to cDNA. + */ + @Test + public void testAlignSequenceAs_withMapping_withUnmappedProtein() + { + + /* + * Exons at codon 2 (AAA) and 4 (TTT) mapped to A and P + */ + final MapList map = new MapList(new int[] + { 4, 6, 10, 12 }, new int[] + { 1, 1, 3, 3 }, 3, 1); + + + /* + * Expect alignment does nothing (aborts realignment). Change this test + * first if different behaviour wanted. + */ + checkAlignSequenceAs("GGGAAACCCTTTGGG", "-A-L-P-", false, + false, map, "GGGAAACCCTTTGGG"); + } + + /** + * Helper method that performs and verifies the method under test. + * + * @param dnaSeq + * @param proteinSeq + * @param preserveMappedGaps + * @param preserveUnmappedGaps + * @param map + * @param expected + */ + protected void checkAlignSequenceAs(final String dnaSeq, + final String proteinSeq, final boolean preserveMappedGaps, + final boolean preserveUnmappedGaps, MapList map, + final String expected) + { + SequenceI dna = new Sequence("Seq1", dnaSeq); + dna.createDatasetSequence(); + SequenceI protein = new Sequence("Seq1", proteinSeq); + protein.createDatasetSequence(); + AlignedCodonFrame acf = new AlignedCodonFrame(); + acf.addMap(dna.getDatasetSequence(), protein.getDatasetSequence(), map); + + AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-', + preserveMappedGaps, preserveUnmappedGaps); + assertEquals(expected, dna.getSequenceAsString()); + } + + /** + * Test for the alignSequenceAs method where we preserve gaps in introns only. + */ + @Test + public void testAlignSequenceAs_keepIntronGapsOnly() + { + + /* + * Intron GGGAAA followed by exon CCCTTT + */ + MapList map = new MapList(new int[] + { 7, 12 }, new int[] + { 1, 2 }, 3, 1); + + checkAlignSequenceAs("GG-G-AA-A-C-CC-T-TT", "AL", + false, true, map, "GG-G-AA-ACCCTTT"); } } diff --git a/test/jalview/analysis/DnaAlignmentGenerator.java b/test/jalview/analysis/DnaAlignmentGenerator.java new file mode 100644 index 0000000..1b8964f --- /dev/null +++ b/test/jalview/analysis/DnaAlignmentGenerator.java @@ -0,0 +1,253 @@ +package jalview.analysis; + +import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.Sequence; +import jalview.datamodel.SequenceI; +import jalview.io.FastaFile; + +import java.util.Arrays; +import java.util.Random; + +/** + * Generates, and outputs in Fasta format, a random DNA alignment for given + * sequence length and count. Will regenerate the same alignment each time if + * the same random seed is used (so may be used for reproducible unit tests). + * Not guaranteed to reproduce the same results between versions, as the rules + * may get tweaked to produce more 'realistic' results. + * + * Arguments: + *
    + *
  • length (number of bases in each sequence)
  • + *
  • height (number of sequences)
  • + *
  • a whole number random seed
  • + *
  • percentage of gaps to include (0-100)
  • + *
  • percentage chance of variation of each position (0-100)
  • + *
+ * + * @author gmcarstairs + * + */ +public class DnaAlignmentGenerator +{ + private static final char GAP = '-'; + + private static final char ZERO = '0'; + + private static final char[] BASES = new char[] + { 'G', 'T', 'C', 'A' }; + + private Random random; + + /** + * Outputs a DNA 'alignment' where each position is a random choice from + * 'GTCA-'. + * + * @param args + */ + public static void main(String[] args) + { + if (args.length != 5) + { + usage(); + return; + } + int width = Integer.parseInt(args[0]); + int height = Integer.parseInt(args[1]); + long randomSeed = Long.valueOf(args[2]); + int gapPercentage = Integer.valueOf(args[3]); + int changePercentage = Integer.valueOf(args[4]); + AlignmentI al = new DnaAlignmentGenerator().generate(width, height, + randomSeed, gapPercentage, changePercentage); + + System.out.println("; " + height + " sequences of " + width + + " bases with " + gapPercentage + "% gaps and " + + changePercentage + "% mutations (random seed = " + randomSeed + + ")"); + System.out.println(new FastaFile().print(al.getSequencesArray())); + } + + /** + * Print parameter help. + */ + private static void usage() + { + System.out.println("Usage:"); + System.out.println("arg0: number of (non-gap) bases per sequence"); + System.out.println("arg1: number sequences"); + System.out + .println("arg2: an integer as random seed (same seed = same results)"); + System.out.println("arg3: percentage of gaps to (randomly) generate"); + System.out + .println("arg4: percentage of 'mutations' to (randomly) generate"); + System.out.println("Example: DnaAlignmentGenerator 12 15 387 10 5"); + System.out + .println("- 15 sequences of 12 bases each, approx 10% gaps and 5% mutations, random seed = 387"); + + } + + /** + * Default constructor + */ + public DnaAlignmentGenerator() + { + + } + + /** + * Outputs a DNA 'alignment' of given width and height, where each position is + * a random choice from 'GTCA-'. + * + * @param width + * @param height + * @param randomSeed + * @param changePercentage + * @param gapPercentage + */ + public AlignmentI generate(int width, int height, long randomSeed, + int gapPercentage, int changePercentage) + { + SequenceI[] seqs = new SequenceI[height]; + random = new Random(randomSeed); + seqs[0] = generateSequence(1, width, gapPercentage); + for (int seqno = 1; seqno < height; seqno++) + { + seqs[seqno] = generateAnotherSequence(seqs[0].getSequence(), + seqno + 1, width, changePercentage); + } + AlignmentI al = new Alignment(seqs); + return al; + } + + /** + * Outputs a DNA 'sequence' of given length, with some random gaps included. + * + * @param seqno + * @param length + * @param gapPercentage + */ + private SequenceI generateSequence(int seqno, int length, + int gapPercentage) + { + StringBuilder seq = new StringBuilder(length); + + /* + * Loop till we've added 'length' bases (excluding gaps) + */ + for (int count = 0; count < length;) + { + boolean addGap = random.nextInt(100) < gapPercentage; + char c = addGap ? GAP : BASES[random.nextInt(Integer.MAX_VALUE) % 4]; + seq.append(c); + if (!addGap) + { + count++; + } + } + final String seqName = "SEQ" + seqno; + final String seqString = seq.toString(); + SequenceI sq = new Sequence(seqName, seqString); + sq.createDatasetSequence(); + return sq; + } + + /** + * Generate a sequence approximately aligned to the first one. + * + * @param ds + * @param seqno + * @param width + * number of bases + * @param changePercentage + * @return + */ + private SequenceI generateAnotherSequence(char[] ds, int seqno, + int width, int changePercentage) + { + int length = ds.length; + char[] seq = new char[length]; + Arrays.fill(seq, ZERO); + int gapsWanted = length - width; + int gapsAdded = 0; + + /* + * First 'randomly' mimic gaps in model sequence. + */ + for (int pos = 0; pos < length; pos++) + { + if (ds[pos] == GAP) + { + /* + * Add a gap at the same position with changePercentage likelihood + */ + seq[pos] = randomCharacter(GAP, changePercentage); + if (seq[pos] == GAP) + { + gapsAdded++; + } + } + } + + /* + * Next scatter any remaining gaps (if any) at random. This gives an even + * distribution. + */ + while (gapsAdded < gapsWanted) + { + boolean added = false; + while (!added) + { + int pos = random.nextInt(length); + if (seq[pos] != GAP) + { + seq[pos] = GAP; + added = true; + gapsAdded++; + } + } + } + + /* + * Finally fill in the rest with randomly mutated bases. + */ + for (int pos = 0; pos < length; pos++) + { + if (seq[pos] == ZERO) + { + char c = randomCharacter(ds[pos], changePercentage); + seq[pos] = c; + } + } + final String seqName = "SEQ" + seqno; + final String seqString = new String(seq); + SequenceI sq = new Sequence(seqName, seqString); + sq.createDatasetSequence(); + return sq; + } + + /** + * Returns a random character that is changePercentage% likely to match the + * given type (as base or gap). + * + * @param changePercentage + * + * @param c + * @return + */ + private char randomCharacter(char c, int changePercentage) + { + final boolean mutation = random.nextInt(100) < changePercentage; + + if (!mutation) + { + return c; + } + + char newchar = c; + while (newchar == c) + { + newchar = BASES[random.nextInt(Integer.MAX_VALUE) % 4]; + } + return newchar; + } +} diff --git a/test/jalview/analysis/DnaTest.java b/test/jalview/analysis/DnaTest.java new file mode 100644 index 0000000..01ed183 --- /dev/null +++ b/test/jalview/analysis/DnaTest.java @@ -0,0 +1,445 @@ +package jalview.analysis; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import jalview.api.AlignViewportI; +import jalview.datamodel.AlignedCodon; +import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.ColumnSelection; +import jalview.datamodel.SequenceI; +import jalview.gui.AlignViewport; +import jalview.io.FormatAdapter; + +import java.io.IOException; + +import org.junit.Test; + +public class DnaTest +{ + // @formatter:off + // AA encoding codons as ordered on the Jalview help page Amino Acid Table + private static String fasta = ">B\n" + "GCT" + "GCC" + "GCA" + "GCG" + + "TGT" + "TGC" + "GAT" + "GAC" + "GAA" + "GAG" + "TTT" + "TTC" + + "GGT" + "GGC" + "GGA" + "GGG" + "CAT" + "CAC" + "ATT" + "ATC" + + "ATA" + "AAA" + "AAG" + "TTG" + "TTA" + "CTT" + "CTC" + "CTA" + + "CTG" + "ATG" + "AAT" + "AAC" + "CCT" + "CCC" + "CCA" + "CCG" + + "CAA" + "CAG" + "CGT" + "CGC" + "CGA" + "CGG" + "AGA" + "AGG" + + "TCT" + "TCC" + "TCA" + "TCG" + "AGT" + "AGC" + "ACT" + "ACC" + + "ACA" + "ACG" + "GTT" + "GTC" + "GTA" + "GTG" + "TGG" + "TAT" + + "TAC" + "TAA" + "TAG" + "TGA"; + + private static String JAL_1312_example_align_fasta = ">B.FR.83.HXB2_LAI_IIIB_BRU_K03455/45-306\n" + + "ATGGGAAAAAATTCGGTTAAGGCCAGGGGGAAAGAAAAAATATAAATTAAAACATATAGTATGGGCAAGCAG\n" + + "GGAGCTAGAACGATTCGCAGTTAATCCTGGCCTGTTAGAAACATCAGAAGGCTGTAGACAAATACTGGGACA\n" + + "GCTACAACCATCCCTTCAGACAGGATCAGAAGAACTTAGATCATTATATAATACAGTAGCAACCCTCTATTG\n" + + "TGTGCATCAAAGGATAGAGATAAAAGACACCAAGGAAGCTTTAGAC\n" + + ">gi|27804621|gb|AY178912.1|/1-259\n" + + "-TGGGAGAA-ATTCGGTT-CGGCCAGGGGGAAAGAAAAAATATCAGTTAAAACATATAGTATGGGCAAGCAG\n" + + "AGAGCTAGAACGATTCGCAGTTAACCCTGGCCTTTTAGAGACATCACAAGGCTGTAGACAAATACTGGGACA\n" + + "GCTACAACCATCCCTTCAGACAGGATCAGAAGAACTTAAATCATTATATAATACAGTAGCAACCCTCTATTG\n" + + "TGTTCATCAAAGGATAGATATAAAAGACACCAAGGAAGCTTTAGAT\n" + + ">gi|27804623|gb|AY178913.1|/1-259\n" + + "-TGGGAGAA-ATTCGGTT-CGGCCAGGGGGAAAGAAAAAATATCAGTTAAAACATATAGTATGGGCAAGCAG\n" + + "AGAGCTAGAACGATTCGCAGTTAACCCTGGCCTTTTAGAGACATCACAAGGCTGTAGACAAATACTGGAACA\n" + + "GCTACAACCATCCCTTCAGACAGGATCAGAAGAACTTAAATCATTATATAATACAGTAGCAACCCTCTATTG\n" + + "TGTTCATCAAAGGATAGATGTAAAAGACACCAAGGAAGCTTTAGAT\n" + + ">gi|27804627|gb|AY178915.1|/1-260\n" + + "-TGGGAAAA-ATTCGGTTAAGGCCAGGGGGAAAGAAAAAATATAAGTTAAAACATATAGTATGGGCAAGCAG\n" + + "GGAGCTAGAACGATTCGCAGTTAACCCTGGCCTGTTAGAAACATCAGAAGGTTGTAGACAAATATTGGGACA\n" + + "GCTACAACCATCCCTTGAGACAGGATCAGAAGAACTTAAATCATTATWTAATACCATAGCAGTCCTCTATTG\n" + + "TGTACATCAAAGGATAGATATAAAAGACACCAAGGAAGCTTTAGAG\n" + + ">gi|27804631|gb|AY178917.1|/1-261\n" + + "-TGGGAAAAAATTCGGTTGAGGCCAGGGGGAAAGAAAAAATATAAGTTAAAACATATAGTATGGGCAAGCAG\n" + + "GGAGCTAGAACGATTCGCAGTCAACCCTGGCCTGTTAGAAACACCAGAAGGCTGTAGACAAATACTGGGACA\n" + + "GCTACAACCGTCCCTTCAGACAGGATCGGAAGAACTTAAATCATTATATAATACAGTAGCAACCCTCTATTG\n" + + "TGTGCATCAAAGGATAGATGTAAAAGACACCAAGGAGGCTTTAGAC\n" + + ">gi|27804635|gb|AY178919.1|/1-261\n" + + "-TGGGAGAGAATTCGGTTACGGCCAGGAGGAAAGAAAAAATATAAATTGAAACATATAGTATGGGCAGGCAG\n" + + "AGAGCTAGATCGATTCGCAGTCAATCCTGGCCTGTTAGAAACATCAGAAGGCTGCAGACAGATATTGGGACA\n" + + "GCTACAACCGTCCCTTAAGACAGGATCAGAAGAACTTAAATCATTATATAATACAGTAGCAACCCTCTATTG\n" + + "TGTACATCAAAGGATAGATGTAAAAGACACCAAGGAAGCTTTAGAT\n" + + ">gi|27804641|gb|AY178922.1|/1-261\n" + + "-TGGGAGAAAATTCGGTTACGGCCAGGGGGAAAGAAAAGATATAAGTTAAAACATATAGTATGGGCAAGCAG\n" + + "GGAGCTAGAACGATTCGCAGTCAACCCTGGCCTGTTAGAAACATCAGAAGGCTGCAGACAAATACTGGGACA\n" + + "GTTACACCCATCCCTTCATACAGGATCAGAAGAACTTAAATCATTATATAATACAGTAGCAACCCTCTATTG\n" + + "TGTGCATCAAAGGATAGAAGTAAAAGACACCAAGGAAGCTTTAGAC\n" + + ">gi|27804647|gb|AY178925.1|/1-261\n" + + "-TGGGAAAAAATTCGGTTAAGGCCAGGGGGAAAGAAAAAATATCAATTAAAACATGTAGTATGGGCAAGCAG\n" + + "GGAACTAGAACGATTCGCAGTTAATCCTGGCCTGTTAGAAACATCAGAAGGCTGTAGACAAATATTGGGACA\n" + + "GCTACAACCATCCCTTCAGACAGGATCAGAGGAACTTAAATCATTATTTAATACAGTAGCAGTCCTCTATTG\n" + + "TGTACATCAAAGAATAGATGTAAAAGACACCAAGGAAGCTCTAGAA\n" + + ">gi|27804649|gb|AY178926.1|/1-261\n" + + "-TGGGAAAAAATTCGGTTAAGGCCAGGGGGAAAGAAAAAATATAAGTTAAAACATATAGTATGGGCAAGCAG\n" + + "GGAGCTAGAACGATTCGCGGTCAATCCTGGCCTGTTAGAAACATCAGAAGGCTGTAGACAACTACTGGGACA\n" + + "GTTACAACCATCCCTTCAGACAGGATCAGAAGAACTCAAATCATTATATAATACAATAGCAACCCTCTATTG\n" + + "TGTGCATCAAAGGATAGAGATAAAAGACACCAAGGAAGCCTTAGAT\n" + + ">gi|27804653|gb|AY178928.1|/1-261\n" + + "-TGGGAAAGAATTCGGTTAAGGCCAGGGGGAAAGAAACAATATAAATTAAAACATATAGTATGGGCAAGCAG\n" + + "GGAGCTAGACCGATTCGCACTTAACCCCGGCCTGTTAGAAACATCAGAAGGCTGTAGACAAATATTGGGACA\n" + + "GCTACAATCGTCCCTTCAGACAGGATCAGAAGAACTTAGATCACTATATAATACAGTAGCAGTCCTCTATTG\n" + + "TGTGCATCAAAAGATAGATGTAAAAGACACCAAGGAAGCCTTAGAC\n" + + ">gi|27804659|gb|AY178931.1|/1-261\n" + + "-TGGGAAAAAATTCGGTTACGGCCAGGAGGAAAGAAAAGATATAAATTAAAACATATAGTATGGGCAAGCAG\n" + + "GGAGCTAGAACGATTYGCAGTTAATCCTGGCCTTTTAGAAACAGCAGAAGGCTGTAGACAAATACTGGGACA\n" + + "GCTACAACCATCCCTTCAGACAGGATCAGAAGAACTTAAATCATTATATAATACAGTAGCAACCCTCTATTG\n" + + "TGTACATCAAAGGATAGAGATAAAAGACACCAAGGAAGCTTTAGAA\n"; + // @formatter:on + + /** + * Corner case for this test is the presence of codons after codons that were + * not translated. + * + * @throws IOException + */ + @Test + public void testTranslateCdna_withUntranslatableCodons() + throws IOException + { + AlignmentI alf = new FormatAdapter().readFile( + JAL_1312_example_align_fasta, jalview.io.FormatAdapter.PASTE, + "FASTA"); + ColumnSelection cs = new ColumnSelection(); + AlignViewportI av = new AlignViewport(alf, cs); + Dna dna = new Dna(av, new int[] + { 0, alf.getWidth() - 1 }); + AlignmentI translated = dna.translateCdna(); + assertNotNull("Couldn't do a full width translation of test data.", + translated); + } + + /** + * Test variant in which 15 column blocks at a time are translated (the rest + * hidden). + * + * @throws IOException + */ + @Test + public void testTranslateCdna_withUntranslatableCodonsAndHiddenColumns() + throws IOException + { + AlignmentI alf = new FormatAdapter().readFile( + JAL_1312_example_align_fasta, jalview.io.FormatAdapter.PASTE, + "FASTA"); + int vwidth = 15; + for (int ipos = 0; ipos + vwidth < alf.getWidth(); ipos += vwidth) + { + ColumnSelection cs = new ColumnSelection(); + if (ipos > 0) + { + cs.hideColumns(0, ipos - 1); + } + cs.hideColumns(ipos + vwidth, alf.getWidth()); + int[] vcontigs = cs.getVisibleContigs(0, alf.getWidth()); + AlignViewportI av = new AlignViewport(alf, cs); + Dna dna = new Dna(av, vcontigs); + AlignmentI transAlf = dna.translateCdna(); + + assertTrue("Translation failed (ipos=" + ipos + + ") No alignment data.", transAlf != null); + assertTrue("Translation failed (ipos=" + ipos + ") Empty alignment.", + transAlf.getHeight() > 0); + assertTrue("Translation failed (ipos=" + ipos + ") Translated " + + transAlf.getHeight() + " sequences from " + alf.getHeight() + + " sequences", alf.getHeight() == transAlf.getHeight()); + } + } + + /** + * Test simple translation to Amino Acids (with STOP codons translated to X). + * + * @throws IOException + */ + @Test + public void testTranslateCdna_simple() throws IOException + { + AlignmentI alf = new FormatAdapter().readFile(fasta, + FormatAdapter.PASTE, "FASTA"); + ColumnSelection cs = new ColumnSelection(); + AlignViewportI av = new AlignViewport(alf, cs); + Dna dna = new Dna(av, new int[] + { 0, alf.getWidth() - 1 }); + AlignmentI translated = dna.translateCdna(); + String aa = translated.getSequenceAt(0).getSequenceAsString(); + assertEquals( + "AAAACCDDEEFFGGGGHHIIIKKLLLLLLMNNPPPPQQRRRRRRSSSSSSTTTTVVVVWYYXXX", + aa); + } + + /** + * Test translation excluding hidden columns. + * + * @throws IOException + */ + @Test + public void testTranslateCdna_hiddenColumns() throws IOException + { + AlignmentI alf = new FormatAdapter().readFile(fasta, + FormatAdapter.PASTE, "FASTA"); + ColumnSelection cs = new jalview.datamodel.ColumnSelection(); + cs.hideColumns(6, 14); // hide codons 3/4/5 + cs.hideColumns(24, 35); // hide codons 9-12 + cs.hideColumns(177, 191); // hide codons 60-64 + AlignViewportI av = new AlignViewport(alf, cs); + Dna dna = new Dna(av, new int[] + { 0, alf.getWidth() - 1 }); + AlignmentI translated = dna.translateCdna(); + String aa = translated.getSequenceAt(0).getSequenceAsString(); + assertEquals("AACDDGGGGHHIIIKKLLLLLLMNNPPPPQQRRRRRRSSSSSSTTTTVVVVW", aa); + } + + /** + * Use this test to help debug into any cases of interest. + */ + @Test + public void testCompareCodonPos_oneOnly() + { + assertFollows("-AA--A", "G--GG"); // 2 shifted seq2, 3 shifted seq1 + } + + /** + * Tests for method that compares 'alignment' of two codon position triplets. + */ + @Test + public void testCompareCodonPos() + { + /* + * Returns 0 for any null argument + */ + assertEquals(0, Dna.compareCodonPos(new AlignedCodon(1, 2, 3), null)); + assertEquals(0, Dna.compareCodonPos(null, new AlignedCodon(1, 2, 3))); + + /* + * Work through 27 combinations. First 9 cases where first position matches. + */ + assertMatches("AAA", "GGG"); // 2 and 3 match + assertFollows("AA-A", "GGG"); // 2 matches, 3 shifted seq1 + assertPrecedes("AAA", "GG-G"); // 2 matches, 3 shifted seq2 + assertFollows("A-AA", "GG-G"); // 2 shifted seq1, 3 matches + assertFollows("A-A-A", "GG-G"); // 2 shifted seq1, 3 shifted seq1 + assertPrecedes("A-AA", "GG--G"); // 2 shifted seq1, 3 shifted seq2 + assertPrecedes("AA-A", "G-GG"); // 2 shifted seq2, 3 matches + assertFollows("AA--A", "G-GG"); // 2 shifted seq2, 3 shifted seq1 + assertPrecedes("AAA", "G-GG"); // 2 shifted seq2, 3 shifted seq2 + + /* + * 9 cases where first position is shifted in first sequence. + */ + assertFollows("-AAA", "G-GG"); // 2 and 3 match + assertFollows("-AA-A", "G-GG"); // 2 matches, 3 shifted seq1 + // 'enclosing' case: pick first to start precedes + assertFollows("-AAA", "G-G-G"); // 2 matches, 3 shifted seq2 + assertFollows("-A-AA", "G-G-G"); // 2 shifted seq1, 3 matches + assertFollows("-A-A-A", "G-G-G"); // 2 shifted seq1, 3 shifted seq1 + // 'enclosing' case: pick first to start precedes + assertFollows("-A-AA", "G-G--G"); // 2 shifted seq1, 3 shifted seq2 + assertFollows("-AA-A", "G--GG"); // 2 shifted seq2, 3 matches + assertFollows("-AA--A", "G--GG"); // 2 shifted seq2, 3 shifted seq1 + assertPrecedes("-AAA", "G--GG"); // 2 shifted seq2, 3 shifted seq2 + + /* + * 9 cases where first position is shifted in second sequence. + */ + assertPrecedes("A-AA", "-GGG"); // 2 and 3 match + assertPrecedes("A-A-A", "-GGG"); // 2 matches, 3 shifted seq1 + assertPrecedes("A-AA", "-GG-G"); // 2 matches, 3 shifted seq2 + assertPrecedes("A--AA", "-GG-G"); // 2 shifted seq1, 3 matches + // 'enclosing' case with middle base deciding: + assertFollows("A--AA", "-GGG"); // 2 shifted seq1, 3 shifted seq1 + assertPrecedes("A--AA", "-GG--G"); // 2 shifted seq1, 3 shifted seq2 + assertPrecedes("AA-A", "-GGG"); // 2 shifted seq2, 3 matches + assertPrecedes("AA--A", "-GGG"); // 2 shifted seq2, 3 shifted seq1 + assertPrecedes("AAA", "-GGG"); // 2 shifted seq2, 3 shifted seq2 + } + + /** + * This test generates a random cDNA alignment and its translation, then + * reorders the cDNA and retranslates, and verifies that the translations are + * the same (apart from ordering). + */ + @Test + public void testTranslateCdna_sequenceOrderIndependent() + { + /* + * Generate cDNA - 8 sequences of 12 bases each. + */ + AlignmentI cdna = new DnaAlignmentGenerator().generate(12, 8, 97, 5, 5); + ColumnSelection cs = new ColumnSelection(); + AlignViewportI av = new AlignViewport(cdna, cs); + Dna dna = new Dna(av, new int[] + { 0, cdna.getWidth() - 1 }); + AlignmentI translated = dna.translateCdna(); + + /* + * Jumble the cDNA sequences and translate. + */ + SequenceI[] sorted = new SequenceI[cdna.getHeight()]; + final int[] jumbler = new int[] + { 6, 7, 3, 4, 2, 0, 1, 5 }; + int seqNo = 0; + for (int i : jumbler) + { + sorted[seqNo++] = cdna.getSequenceAt(i); + } + AlignmentI cdnaReordered = new Alignment(sorted); + av = new AlignViewport(cdnaReordered, cs); + dna = new Dna(av, new int[] + { 0, cdna.getWidth() - 1 }); + AlignmentI translated2 = dna.translateCdna(); + + /* + * Check translated sequences are the same in both alignments. + */ + System.out.println("Original"); + System.out.println(translated.toString()); + System.out.println("Sorted"); + System.out.println(translated2.toString()); + + int sortedSequenceIndex = 0; + for (int originalSequenceIndex : jumbler) + { + final String translation1 = translated.getSequenceAt( + originalSequenceIndex).getSequenceAsString(); + final String translation2 = translated2.getSequenceAt(sortedSequenceIndex) + .getSequenceAsString(); + assertEquals(translation2, translation1); + sortedSequenceIndex++; + } + } + + /** + * Test that all the cases in testCompareCodonPos have a 'symmetric' + * comparison (without checking the actual comparison result). + */ + @Test + public void testCompareCodonPos_isSymmetric() + { + assertSymmetric("AAA", "GGG"); + assertSymmetric("AA-A", "GGG"); + assertSymmetric("AAA", "GG-G"); + assertSymmetric("A-AA", "GG-G"); + assertSymmetric("A-A-A", "GG-G"); + assertSymmetric("A-AA", "GG--G"); + assertSymmetric("AA-A", "G-GG"); + assertSymmetric("AA--A", "G-GG"); + assertSymmetric("AAA", "G-GG"); + assertSymmetric("-AAA", "G-GG"); + assertSymmetric("-AA-A", "G-GG"); + assertSymmetric("-AAA", "G-G-G"); + assertSymmetric("-A-AA", "G-G-G"); + assertSymmetric("-A-A-A", "G-G-G"); + assertSymmetric("-A-AA", "G-G--G"); + assertSymmetric("-AA-A", "G--GG"); + assertSymmetric("-AA--A", "G--GG"); + assertSymmetric("-AAA", "G--GG"); + assertSymmetric("A-AA", "-GGG"); + assertSymmetric("A-A-A", "-GGG"); + assertSymmetric("A-AA", "-GG-G"); + assertSymmetric("A--AA", "-GG-G"); + assertSymmetric("A--AA", "-GGG"); + assertSymmetric("A--AA", "-GG--G"); + assertSymmetric("AA-A", "-GGG"); + assertSymmetric("AA--A", "-GGG"); + assertSymmetric("AAA", "-GGG"); + } + + private void assertSymmetric(String codon1, String codon2) + { + assertEquals("Comparison of '" + codon1 + "' and '" + codon2 + + " not symmetric", Integer.signum(compare(codon1, codon2)), + -Integer.signum(compare(codon2, codon1))); + } + + /** + * Assert that the first sequence should map to the same position as the + * second in a translated alignment. Also checks that this is true if the + * order of the codons is reversed. + * + * @param codon1 + * @param codon2 + */ + private void assertMatches(String codon1, String codon2) + { + assertEquals("Expected '" + codon1 + "' matches '" + codon2 + "'", 0, + compare(codon1, codon2)); + assertEquals("Expected '" + codon2 + "' matches '" + codon1 + "'", 0, + compare(codon2, codon1)); + } + + /** + * Assert that the first sequence should precede the second in a translated + * alignment + * + * @param codon1 + * @param codon2 + */ + private void assertPrecedes(String codon1, String codon2) + { + assertEquals("Expected '" + codon1 + "' precedes '" + codon2 + "'", + -1, compare(codon1, codon2)); + } + + /** + * Assert that the first sequence should follow the second in a translated + * alignment + * + * @param codon1 + * @param codon2 + */ + private void assertFollows(String codon1, String codon2) + { + assertEquals("Expected '" + codon1 + "' follows '" + codon2 + "'", 1, + compare(codon1, codon2)); + } + + /** + * Convert two nucleotide strings to base positions and pass to + * Dna.compareCodonPos, return the result. + * + * @param s1 + * @param s2 + * @return + */ + private int compare(String s1, String s2) + { + final AlignedCodon cd1 = convertCodon(s1); + final AlignedCodon cd2 = convertCodon(s2); + System.out.println("K: " + s1 + " " + cd1.toString()); + System.out.println("G: " + s2 + " " + cd2.toString()); + System.out.println(); + return Dna.compareCodonPos(cd1, cd2); + } + + /** + * Convert a string e.g. "-GC-T" to base positions e.g. [1, 2, 4]. The string + * should have exactly 3 non-gap characters, and use '-' for gaps. + * + * @param s + * @return + */ + private AlignedCodon convertCodon(String s) + { + int[] codon = new int[3]; + int i = 0; + for (int j = 0; j < s.length(); j++) + { + if (s.charAt(j) != '-') + { + codon[i++] = j; + } + } + return new AlignedCodon(codon[0], codon[1], codon[2]); + } + + /** + * Weirdly, maybe worth a test to prove the helper method of this test class. + */ + @Test + public void testConvertCodon() + { + assertEquals("[0, 1, 2]", convertCodon("AAA").toString()); + assertEquals("[0, 2, 5]", convertCodon("A-A--A").toString()); + assertEquals("[1, 3, 4]", convertCodon("-A-AA-").toString()); + } +} diff --git a/test/jalview/analysis/DnaTranslation.java b/test/jalview/analysis/DnaTranslation.java deleted file mode 100644 index 708ee21..0000000 --- a/test/jalview/analysis/DnaTranslation.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2) - * Copyright (C) 2014 The Jalview Authors - * - * This file is part of Jalview. - * - * Jalview is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 - * of the License, or (at your option) any later version. - * - * Jalview is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Jalview. If not, see . - * The Jalview Authors are detailed in the 'AUTHORS' file. - */ -package jalview.analysis; - -import static org.junit.Assert.*; -import jalview.datamodel.ColumnSelection; - -import java.io.IOException; - -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; - -public class DnaTranslation -{ - - private static String JAL_1312_example_align_fasta = ">B.FR.83.HXB2_LAI_IIIB_BRU_K03455/45-306\n" - + "ATGGGAAAAAATTCGGTTAAGGCCAGGGGGAAAGAAAAAATATAAATTAAAACATATAGTATGGGCAAGCAG\n" - + "GGAGCTAGAACGATTCGCAGTTAATCCTGGCCTGTTAGAAACATCAGAAGGCTGTAGACAAATACTGGGACA\n" - + "GCTACAACCATCCCTTCAGACAGGATCAGAAGAACTTAGATCATTATATAATACAGTAGCAACCCTCTATTG\n" - + "TGTGCATCAAAGGATAGAGATAAAAGACACCAAGGAAGCTTTAGAC\n" - + ">gi|27804621|gb|AY178912.1|/1-259\n" - + "-TGGGAGAA-ATTCGGTT-CGGCCAGGGGGAAAGAAAAAATATCAGTTAAAACATATAGTATGGGCAAGCAG\n" - + "AGAGCTAGAACGATTCGCAGTTAACCCTGGCCTTTTAGAGACATCACAAGGCTGTAGACAAATACTGGGACA\n" - + "GCTACAACCATCCCTTCAGACAGGATCAGAAGAACTTAAATCATTATATAATACAGTAGCAACCCTCTATTG\n" - + "TGTTCATCAAAGGATAGATATAAAAGACACCAAGGAAGCTTTAGAT\n" - + ">gi|27804623|gb|AY178913.1|/1-259\n" - + "-TGGGAGAA-ATTCGGTT-CGGCCAGGGGGAAAGAAAAAATATCAGTTAAAACATATAGTATGGGCAAGCAG\n" - + "AGAGCTAGAACGATTCGCAGTTAACCCTGGCCTTTTAGAGACATCACAAGGCTGTAGACAAATACTGGAACA\n" - + "GCTACAACCATCCCTTCAGACAGGATCAGAAGAACTTAAATCATTATATAATACAGTAGCAACCCTCTATTG\n" - + "TGTTCATCAAAGGATAGATGTAAAAGACACCAAGGAAGCTTTAGAT\n" - + ">gi|27804627|gb|AY178915.1|/1-260\n" - + "-TGGGAAAA-ATTCGGTTAAGGCCAGGGGGAAAGAAAAAATATAAGTTAAAACATATAGTATGGGCAAGCAG\n" - + "GGAGCTAGAACGATTCGCAGTTAACCCTGGCCTGTTAGAAACATCAGAAGGTTGTAGACAAATATTGGGACA\n" - + "GCTACAACCATCCCTTGAGACAGGATCAGAAGAACTTAAATCATTATWTAATACCATAGCAGTCCTCTATTG\n" - + "TGTACATCAAAGGATAGATATAAAAGACACCAAGGAAGCTTTAGAG\n" - + ">gi|27804631|gb|AY178917.1|/1-261\n" - + "-TGGGAAAAAATTCGGTTGAGGCCAGGGGGAAAGAAAAAATATAAGTTAAAACATATAGTATGGGCAAGCAG\n" - + "GGAGCTAGAACGATTCGCAGTCAACCCTGGCCTGTTAGAAACACCAGAAGGCTGTAGACAAATACTGGGACA\n" - + "GCTACAACCGTCCCTTCAGACAGGATCGGAAGAACTTAAATCATTATATAATACAGTAGCAACCCTCTATTG\n" - + "TGTGCATCAAAGGATAGATGTAAAAGACACCAAGGAGGCTTTAGAC\n" - + ">gi|27804635|gb|AY178919.1|/1-261\n" - + "-TGGGAGAGAATTCGGTTACGGCCAGGAGGAAAGAAAAAATATAAATTGAAACATATAGTATGGGCAGGCAG\n" - + "AGAGCTAGATCGATTCGCAGTCAATCCTGGCCTGTTAGAAACATCAGAAGGCTGCAGACAGATATTGGGACA\n" - + "GCTACAACCGTCCCTTAAGACAGGATCAGAAGAACTTAAATCATTATATAATACAGTAGCAACCCTCTATTG\n" - + "TGTACATCAAAGGATAGATGTAAAAGACACCAAGGAAGCTTTAGAT\n" - + ">gi|27804641|gb|AY178922.1|/1-261\n" - + "-TGGGAGAAAATTCGGTTACGGCCAGGGGGAAAGAAAAGATATAAGTTAAAACATATAGTATGGGCAAGCAG\n" - + "GGAGCTAGAACGATTCGCAGTCAACCCTGGCCTGTTAGAAACATCAGAAGGCTGCAGACAAATACTGGGACA\n" - + "GTTACACCCATCCCTTCATACAGGATCAGAAGAACTTAAATCATTATATAATACAGTAGCAACCCTCTATTG\n" - + "TGTGCATCAAAGGATAGAAGTAAAAGACACCAAGGAAGCTTTAGAC\n" - + ">gi|27804647|gb|AY178925.1|/1-261\n" - + "-TGGGAAAAAATTCGGTTAAGGCCAGGGGGAAAGAAAAAATATCAATTAAAACATGTAGTATGGGCAAGCAG\n" - + "GGAACTAGAACGATTCGCAGTTAATCCTGGCCTGTTAGAAACATCAGAAGGCTGTAGACAAATATTGGGACA\n" - + "GCTACAACCATCCCTTCAGACAGGATCAGAGGAACTTAAATCATTATTTAATACAGTAGCAGTCCTCTATTG\n" - + "TGTACATCAAAGAATAGATGTAAAAGACACCAAGGAAGCTCTAGAA\n" - + ">gi|27804649|gb|AY178926.1|/1-261\n" - + "-TGGGAAAAAATTCGGTTAAGGCCAGGGGGAAAGAAAAAATATAAGTTAAAACATATAGTATGGGCAAGCAG\n" - + "GGAGCTAGAACGATTCGCGGTCAATCCTGGCCTGTTAGAAACATCAGAAGGCTGTAGACAACTACTGGGACA\n" - + "GTTACAACCATCCCTTCAGACAGGATCAGAAGAACTCAAATCATTATATAATACAATAGCAACCCTCTATTG\n" - + "TGTGCATCAAAGGATAGAGATAAAAGACACCAAGGAAGCCTTAGAT\n" - + ">gi|27804653|gb|AY178928.1|/1-261\n" - + "-TGGGAAAGAATTCGGTTAAGGCCAGGGGGAAAGAAACAATATAAATTAAAACATATAGTATGGGCAAGCAG\n" - + "GGAGCTAGACCGATTCGCACTTAACCCCGGCCTGTTAGAAACATCAGAAGGCTGTAGACAAATATTGGGACA\n" - + "GCTACAATCGTCCCTTCAGACAGGATCAGAAGAACTTAGATCACTATATAATACAGTAGCAGTCCTCTATTG\n" - + "TGTGCATCAAAAGATAGATGTAAAAGACACCAAGGAAGCCTTAGAC\n" - + ">gi|27804659|gb|AY178931.1|/1-261\n" - + "-TGGGAAAAAATTCGGTTACGGCCAGGAGGAAAGAAAAGATATAAATTAAAACATATAGTATGGGCAAGCAG\n" - + "GGAGCTAGAACGATTYGCAGTTAATCCTGGCCTTTTAGAAACAGCAGAAGGCTGTAGACAAATACTGGGACA\n" - + "GCTACAACCATCCCTTCAGACAGGATCAGAAGAACTTAAATCATTATATAATACAGTAGCAACCCTCTATTG\n" - + "TGTACATCAAAGGATAGAGATAAAAGACACCAAGGAAGCTTTAGAA\n"; - - @Test - public void translationWithUntranslatableCodonsTest() - { - // Corner case for this test is the presence of codons after codons that - // were not translated. - jalview.datamodel.AlignmentI alf = null; - try - { - alf = new jalview.io.FormatAdapter().readFile( - JAL_1312_example_align_fasta, jalview.io.FormatAdapter.PASTE, - "FASTA"); - } catch (IOException x) - { - x.printStackTrace(); - fail("Unexpected IOException (" + x.getMessage() - + ") - check test environment"); - } - { - // full translation - ColumnSelection cs = new jalview.datamodel.ColumnSelection(); - assertNotNull("Couldn't do a full width translation of test data.", - jalview.analysis.Dna.CdnaTranslate( - alf.getSequencesArray(), - cs.getVisibleSequenceStrings(0, alf.getWidth(), - alf.getSequencesArray()), new int[] - { 0, alf.getWidth() - 1 }, alf.getGapCharacter(), - null, alf.getWidth(), null)); - } - int vwidth = 15; // translate in 15 base stretches - for (int ipos = 0; ipos + vwidth < alf.getWidth(); ipos += vwidth) - { - ColumnSelection cs = new jalview.datamodel.ColumnSelection(); - if (ipos > 0) - { - cs.hideColumns(0, ipos - 1); - } - cs.hideColumns(ipos + vwidth, alf.getWidth()); - int[] vcontigs = cs.getVisibleContigs(0, alf.getWidth()); - String[] sel = cs.getVisibleSequenceStrings(0, alf.getWidth(), - alf.getSequencesArray()); - jalview.datamodel.AlignmentI transAlf = jalview.analysis.Dna - .CdnaTranslate(alf.getSequencesArray(), sel, vcontigs, - alf.getGapCharacter(), null, alf.getWidth(), null); - - assertTrue("Translation failed (ipos=" + ipos - + ") No alignment data.", transAlf != null); - assertTrue("Translation failed (ipos=" + ipos + ") Empty algnment.", - transAlf.getHeight() > 0); - assertTrue("Translation failed (ipos=" + ipos + ") Translated " - + transAlf.getHeight() + " sequences from " + alf.getHeight() - + " sequences", alf.getHeight() == transAlf.getHeight()); - } - - } -} diff --git a/test/jalview/analysis/TestAlignSeq.java b/test/jalview/analysis/TestAlignSeq.java index ca4f18d..6f331ff 100644 --- a/test/jalview/analysis/TestAlignSeq.java +++ b/test/jalview/analysis/TestAlignSeq.java @@ -20,7 +20,9 @@ */ package jalview.analysis; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import jalview.datamodel.Mapping; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceI; @@ -74,4 +76,13 @@ public class TestAlignSeq } } + @Test + public void testExtractGaps() + { + assertNull(AlignSeq.extractGaps(null, null)); + assertNull(AlignSeq.extractGaps(". -", null)); + assertNull(AlignSeq.extractGaps(null, "AB-C")); + + assertEquals("ABCD", AlignSeq.extractGaps(" .-", ". -A-B.C D.")); + } } diff --git a/test/jalview/commands/EditCommandTest.java b/test/jalview/commands/EditCommandTest.java index fc821b9..6ea05e6 100644 --- a/test/jalview/commands/EditCommandTest.java +++ b/test/jalview/commands/EditCommandTest.java @@ -1,6 +1,7 @@ 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; @@ -8,6 +9,8 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceI; +import java.util.Map; + import org.junit.Before; import org.junit.Ignore; import org.junit.Test; @@ -33,9 +36,13 @@ public class EditCommandTest 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('?'); } @@ -227,6 +234,373 @@ public class EditCommandTest 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. + *
    + *
  • Start: ABCDEF insert 2 at pos 1
  • + *
  • next: A--BCDEF insert 1 at pos 4
  • + *
  • next: A--B-CDEF insert 2 at pos 0
  • + *
  • last: --A--B-CDEF
  • + *
+ */ + @Test + public void testPriorState_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 unwound = command.priorState(false); + assertEquals("ABCDEF", unwound.get(ds).getSequenceAsString()); + } + + /** + * Test for 'undoing' a series of gap deletions. + *
    + *
  • Start: A-B-C delete 1 at pos 1
  • + *
  • Next: AB-C delete 1 at pos 2
  • + *
  • End: ABC
  • + *
+ */ + @Test + public void testPriorState_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 unwound = command.priorState(false); + assertEquals("A-B-C", unwound.get(ds).getSequenceAsString()); + } + + /** + * Test for 'undoing' a single delete edit. + */ + @Test + public void testPriorState_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 unwound = command.priorState(false); + assertEquals("AB--CDEF", unwound.get(ds).getSequenceAsString()); + } + + /** + * Test 'undoing' a single gap insertion edit command. + */ + @Test + public void testPriorState_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 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 testPriorState_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 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 testPriorState_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 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()); } } diff --git a/test/jalview/datamodel/AlignedCodonFrameTest.java b/test/jalview/datamodel/AlignedCodonFrameTest.java new file mode 100644 index 0000000..25d0155 --- /dev/null +++ b/test/jalview/datamodel/AlignedCodonFrameTest.java @@ -0,0 +1,112 @@ +package jalview.datamodel; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import jalview.util.MapList; + +import java.util.Arrays; + +import org.junit.Test; + +public class AlignedCodonFrameTest +{ + + /** + * Test the method that locates the first aligned sequence that has a mapping. + */ + @Test + public void testFindAlignedSequence() + { + AlignmentI cdna = new Alignment(new SequenceI[] + {}); + final Sequence seq1 = new Sequence("Seq1", "C-G-TA-GC"); + seq1.createDatasetSequence(); + cdna.addSequence(seq1); + final Sequence seq2 = new Sequence("Seq2", "-TA-GG-GG"); + seq2.createDatasetSequence(); + cdna.addSequence(seq2); + + AlignmentI aa = new Alignment(new SequenceI[] + {}); + final Sequence aseq1 = new Sequence("Seq1", "-P-R"); + aseq1.createDatasetSequence(); + aa.addSequence(aseq1); + final Sequence aseq2 = new Sequence("Seq2", "-LY-"); + aseq2.createDatasetSequence(); + aa.addSequence(aseq2); + + /* + * Mapping from first DNA sequence to second AA sequence. + */ + AlignedCodonFrame acf = new AlignedCodonFrame(); + + assertNull(acf.findAlignedSequence(seq1, aa)); + + MapList map = new MapList(new int[] + { 1, 6 }, new int[] + { 1, 2 }, 3, 1); + acf.addMap(seq1.getDatasetSequence(), aseq2.getDatasetSequence(), map); + + /* + * DNA seq1 maps to AA seq2 + */ + assertEquals(aa.getSequenceAt(1), + acf.findAlignedSequence(cdna + .getSequenceAt(0).getDatasetSequence(), aa)); + + assertEquals(cdna.getSequenceAt(0), + acf.findAlignedSequence(aa + .getSequenceAt(1).getDatasetSequence(), cdna)); + } + + /** + * Test the method that locates the mapped codon for a protein position. + */ + @Test + public void testGetMappedRegion() + { + // introns lower case, exons upper case + final Sequence seq1 = new Sequence("Seq1", "c-G-TA-gC-gT-T"); + seq1.createDatasetSequence(); + final Sequence seq2 = new Sequence("Seq2", "-TA-gG-Gg-CG-a"); + seq2.createDatasetSequence(); + + final Sequence aseq1 = new Sequence("Seq1", "-P-R"); + aseq1.createDatasetSequence(); + final Sequence aseq2 = new Sequence("Seq2", "-LY-"); + aseq2.createDatasetSequence(); + + /* + * First with no mappings + */ + AlignedCodonFrame acf = new AlignedCodonFrame(); + + assertNull(acf.getMappedRegion(seq1, aseq1, 1)); + + /* + * Set up the mappings for the exons (upper-case bases) + */ + MapList map = new MapList(new int[] + { 2, 4, 6, 6, 8, 9 }, new int[] + { 1, 2 }, 3, 1); + acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map); + map = new MapList(new int[] + { 1, 2, 4, 5, 7, 8 }, new int[] + { 1, 2 }, 3, 1); + acf.addMap(seq2.getDatasetSequence(), aseq2.getDatasetSequence(), map); + + assertEquals("[2, 4]", + Arrays.toString(acf.getMappedRegion(seq1, aseq1, 1))); + assertEquals("[6, 6, 8, 9]", + Arrays.toString(acf.getMappedRegion(seq1, aseq1, 2))); + assertEquals("[1, 2, 4, 4]", + Arrays.toString(acf.getMappedRegion(seq2, aseq2, 1))); + assertEquals("[5, 5, 7, 8]", + Arrays.toString(acf.getMappedRegion(seq2, aseq2, 2))); + + /* + * No mapping from sequence 1 to sequence 2 + */ + assertNull(acf.getMappedRegion(seq1, aseq2, 1)); + } +} diff --git a/test/jalview/datamodel/AlignedCodonTest.java b/test/jalview/datamodel/AlignedCodonTest.java new file mode 100644 index 0000000..60368b1 --- /dev/null +++ b/test/jalview/datamodel/AlignedCodonTest.java @@ -0,0 +1,28 @@ +package jalview.datamodel; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class AlignedCodonTest +{ + + @Test + public void testEquals() + { + AlignedCodon ac = new AlignedCodon(1, 3, 4); + assertTrue(ac.equals(null)); + assertFalse(ac.equals("hello")); + assertFalse(ac.equals(new AlignedCodon(1, 3, 5))); + assertTrue(ac.equals(new AlignedCodon(1, 3, 4))); + assertTrue(ac.equals(ac)); + } + + @Test + public void testToString() { + AlignedCodon ac = new AlignedCodon(1, 3, 4); + assertEquals("[1, 3, 4]", ac.toString()); + } +} diff --git a/test/jalview/datamodel/AlignmentTest.java b/test/jalview/datamodel/AlignmentTest.java index 93170b7..3b3d926 100644 --- a/test/jalview/datamodel/AlignmentTest.java +++ b/test/jalview/datamodel/AlignmentTest.java @@ -4,6 +4,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import jalview.io.AppletFormatAdapter; +import jalview.io.FormatAdapter; +import jalview.util.MapList; import java.io.IOException; import java.util.Iterator; @@ -32,10 +34,45 @@ public class AlignmentTest "D.melanogaster.3 G.UGGCGCU..UAUGACGCA\n" + "#=GR D.melanogaster.3 SS (.(((...(....(((((((\n" + "//"; + + private static final String AA_SEQS_1 = + ">Seq1Name\n" + + "K-QY--L\n" + + ">Seq2Name\n" + + "-R-FP-W-\n"; + + private static final String CDNA_SEQS_1 = + ">Seq1Name\n" + + "AC-GG--CUC-CAA-CT\n" + + ">Seq2Name\n" + + "-CG-TTA--ACG---AAGT\n"; + + private static final String CDNA_SEQS_2 = + ">Seq1Name\n" + + "GCTCGUCGTACT\n" + + ">Seq2Name\n" + + "GGGTCAGGCAGT\n"; // @formatter:on + private AlignmentI al; - private Alignment al; + /** + * Helper method to load an alignment and ensure dataset sequences are set up. + * + * @param data + * @param format + * TODO + * @return + * @throws IOException + */ + protected AlignmentI loadAlignment(final String data, String format) + throws IOException + { + Alignment a = new FormatAdapter().readFile(data, + AppletFormatAdapter.PASTE, format); + a.setDataset(null); + return a; + } /* * Read in Stockholm format test data including secondary structure @@ -44,13 +81,12 @@ public class AlignmentTest @Before public void setUp() throws IOException { - al = new jalview.io.FormatAdapter().readFile(TEST_DATA, - AppletFormatAdapter.PASTE, "STH"); - for (int i = 0; i < al.getSequencesArray().length; ++i) + al = loadAlignment(TEST_DATA, "STH"); + int i = 0; + for (AlignmentAnnotation ann : al.getAlignmentAnnotation()) { - al.addAnnotation(al.getSequenceAt(i).getAnnotation()[0]); - al.getSequenceAt(i).getAnnotation()[0].setCalcId("CalcIdFor" - + al.getSequenceAt(i).getName()); + ann.setCalcId("CalcIdFor" + al.getSequenceAt(i).getName()); + i++; } } @@ -68,4 +104,174 @@ public class AlignmentTest assertEquals("D.melanogaster.2", ann.sequenceRef.getName()); assertFalse(iter.hasNext()); } + + @Test + public void testDeleteAllAnnotations_includingAutocalculated() + { + AlignmentAnnotation aa = new AlignmentAnnotation("Consensus", + "Consensus", 0.5); + aa.autoCalculated = true; + al.addAnnotation(aa); + AlignmentAnnotation[] anns = al.getAlignmentAnnotation(); + assertEquals("Wrong number of annotations before deleting", 4, + anns.length); + al.deleteAllAnnotations(true); + assertEquals("Not all deleted", 0, al.getAlignmentAnnotation().length); + } + + @Test + public void testDeleteAllAnnotations_excludingAutocalculated() + { + AlignmentAnnotation aa = new AlignmentAnnotation("Consensus", + "Consensus", 0.5); + aa.autoCalculated = true; + al.addAnnotation(aa); + AlignmentAnnotation[] anns = al.getAlignmentAnnotation(); + assertEquals("Wrong number of annotations before deleting", 4, + anns.length); + al.deleteAllAnnotations(false); + assertEquals("Not just one annotation left", 1, + al.getAlignmentAnnotation().length); + } + + /** + * Tests for realigning as per a supplied alignment: Dna as Dna. + * + * Note: AlignedCodonFrame's state variables are named for protein-to-cDNA + * mapping, but can be exploited for a general 'sequence-to-sequence' mapping + * as here. + * + * @throws IOException + */ + @Test + public void testAlignAs_dnaAsDna() throws IOException + { + // aligned cDNA: + AlignmentI al1 = loadAlignment(CDNA_SEQS_1, "FASTA"); + // unaligned cDNA: + AlignmentI al2 = loadAlignment(CDNA_SEQS_2, "FASTA"); + + /* + * Make mappings between sequences. The 'aligned cDNA' is playing the role + * of what would normally be protein here. + */ + AlignedCodonFrame acf = new AlignedCodonFrame(); + MapList ml = new MapList(new int[] + { 1, 12 }, new int[] + { 1, 12 }, 1, 1); + acf.addMap(al2.getSequenceAt(0), al1.getSequenceAt(0), ml); + acf.addMap(al2.getSequenceAt(1), al1.getSequenceAt(1), ml); + al1.addCodonFrame(acf); + + ((Alignment) al2).alignAs(al1, false, true); + assertEquals("GC-TC--GUC-GTA-CT", al2.getSequenceAt(0) + .getSequenceAsString()); + assertEquals("-GG-GTC--AGG---CAGT", al2.getSequenceAt(1) + .getSequenceAsString()); + } + + /** + * Aligning protein from cDNA yet to be implemented, does nothing. + * + * @throws IOException + */ + @Test + public void testAlignAs_proteinAsCdna() throws IOException + { + AlignmentI al1 = loadAlignment(CDNA_SEQS_1, "FASTA"); + AlignmentI al2 = loadAlignment(AA_SEQS_1, "FASTA"); + String before0 = al2.getSequenceAt(0).getSequenceAsString(); + String before1 = al2.getSequenceAt(1).getSequenceAsString(); + + ((Alignment) al2).alignAs(al1, false, true); + assertEquals(before0, al2.getSequenceAt(0).getSequenceAsString()); + assertEquals(before1, al2.getSequenceAt(1).getSequenceAsString()); + } + + /** + * Test aligning cdna as per protein alignment. + * + * @throws IOException + */ + @Test + public void testAlignAs_cdnaAsProtein() throws IOException + { + /* + * Load alignments and add mappings for cDNA to protein + */ + AlignmentI al1 = loadAlignment(CDNA_SEQS_1, "FASTA"); + AlignmentI al2 = loadAlignment(AA_SEQS_1, "FASTA"); + AlignedCodonFrame acf = new AlignedCodonFrame(); + MapList ml = new MapList(new int[] + { 1, 12 }, new int[] + { 1, 4 }, 3, 1); + acf.addMap(al1.getSequenceAt(0), al2.getSequenceAt(0), ml); + acf.addMap(al1.getSequenceAt(1), al2.getSequenceAt(1), ml); + al2.addCodonFrame(acf); + + /* + * Realign DNA; currently keeping existing gaps in introns only + */ + ((Alignment) al1).alignAs(al2, false, true); + assertEquals("ACG---GCUCCA------ACT", al1.getSequenceAt(0) + .getSequenceAsString()); + assertEquals("---CGT---TAACGA---AGT", al1.getSequenceAt(1) + .getSequenceAsString()); + } + + /** + * Test aligning dna as per protein alignment, for the case where there are + * introns (i.e. some dna sites have no mapping from a peptide). + * + * @throws IOException + */ + @Test + public void testAlignAs_dnaAsProtein_withIntrons() throws IOException + { + /* + * Load alignments and add mappings for cDNA to protein + */ + String dna1 = "A-Aa-gG-GCC-cT-TT"; + String dna2 = "c--CCGgg-TT--T-AA-A"; + AlignmentI al1 = loadAlignment(">Seq1\n" + dna1 + "\n>Seq2\n" + dna2 + + "\n", "FASTA"); + AlignmentI al2 = loadAlignment(">Seq1\n-P--YK\n>Seq2\nG-T--F\n", + "FASTA"); + AlignedCodonFrame acf = new AlignedCodonFrame(); + // Seq1 has intron at dna positions 3,4,9 so splice is AAG GCC TTT + // Seq2 has intron at dna positions 1,5,6 so splice is CCG TTT AAA + MapList ml1 = new MapList(new int[] + { 1, 2, 5, 8, 10, 12 }, new int[] + { 1, 3 }, 3, 1); + acf.addMap(al1.getSequenceAt(0), al2.getSequenceAt(0), ml1); + MapList ml2 = new MapList(new int[] + { 2, 4, 7, 12 }, new int[] + { 1, 3 }, 3, 1); + acf.addMap(al1.getSequenceAt(1), al2.getSequenceAt(1), ml2); + al2.addCodonFrame(acf); + + /* + * Align ignoring gaps in dna introns and exons + */ + ((Alignment) al1).alignAs(al2, false, false); + assertEquals("---AAagG------GCCcTTT", al1.getSequenceAt(0) + .getSequenceAsString()); + // note 1 gap in protein corresponds to 'gg-' in DNA (3 positions) + assertEquals("cCCGgg-TTT------AAA", al1.getSequenceAt(1) + .getSequenceAsString()); + + /* + * Reset and realign, preserving gaps in dna introns and exons + */ + al1.getSequenceAt(0).setSequence(dna1); + al1.getSequenceAt(1).setSequence(dna2); + ((Alignment) al1).alignAs(al2, true, true); + // String dna1 = "A-Aa-gG-GCC-cT-TT"; + // String dna2 = "c--CCGgg-TT--T-AA-A"; + // assumption: we include 'the greater of' protein/dna gap lengths, not both + assertEquals("---A-Aa-gG------GCC-cT-TT", al1.getSequenceAt(0) + .getSequenceAsString()); + assertEquals("c--CCGgg-TT--T------AA-A", al1.getSequenceAt(1) + .getSequenceAsString()); + } } diff --git a/test/jalview/datamodel/ColumnSelectionTest.java b/test/jalview/datamodel/ColumnSelectionTest.java new file mode 100644 index 0000000..228156a --- /dev/null +++ b/test/jalview/datamodel/ColumnSelectionTest.java @@ -0,0 +1,48 @@ +package jalview.datamodel; + +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.junit.Test; + +public class ColumnSelectionTest +{ + + @Test + public void testAddElement() + { + ColumnSelection cs = new ColumnSelection(); + cs.addElement(2); + cs.addElement(5); + List sel = cs.getSelected(); + assertEquals(2, sel.size()); + assertEquals(new Integer(2), sel.get(0)); + assertEquals(new Integer(5), sel.get(1)); + } + + /** + * Test the remove method - in particular to verify that remove(int i) removes + * the element whose value is i, _NOT_ the i'th element. + */ + @Test + public void testRemoveElement() + { + ColumnSelection cs = new ColumnSelection(); + cs.addElement(2); + cs.addElement(5); + + // removing elements not in the list has no effect + cs.removeElement(0); + cs.removeElement(1); + List sel = cs.getSelected(); + assertEquals(2, sel.size()); + assertEquals(new Integer(2), sel.get(0)); + assertEquals(new Integer(5), sel.get(1)); + + // removing an element in the list removes it + cs.removeElement(2); + assertEquals(1, sel.size()); + assertEquals(new Integer(5), sel.get(0)); + } +} diff --git a/test/jalview/datamodel/SequenceTest.java b/test/jalview/datamodel/SequenceTest.java index 40476a0..2a2d2ab 100644 --- a/test/jalview/datamodel/SequenceTest.java +++ b/test/jalview/datamodel/SequenceTest.java @@ -5,6 +5,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import java.util.Arrays; import java.util.List; import org.junit.Before; @@ -135,4 +136,130 @@ public class SequenceTest 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()); // ?? + } + + /** + * Tests for the method that returns an alignment column position (base 1) for + * a given sequence position (base 1). + */ + @Test + public void testFindIndex() + { + SequenceI seq = new Sequence("test", "ABCDEF"); + assertEquals(0, seq.findIndex(0)); + assertEquals(1, seq.findIndex(1)); + assertEquals(5, seq.findIndex(5)); + assertEquals(6, seq.findIndex(6)); + assertEquals(6, seq.findIndex(9)); + + seq = new Sequence("test", "-A--B-C-D-E-F--"); + assertEquals(2, seq.findIndex(1)); + assertEquals(5, seq.findIndex(2)); + assertEquals(7, seq.findIndex(3)); + + // before start returns 0 + assertEquals(0, seq.findIndex(0)); + assertEquals(0, seq.findIndex(-1)); + + // beyond end returns last residue column + assertEquals(13, seq.findIndex(99)); + + } + + /** + * Tests for the method that returns a dataset sequence position (base 1) for + * an aligned column position (base 0). + */ + @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 + } + + /** + * Test the method that returns an array of aligned sequence positions where + * the array index is the data sequence position (both base 0). + */ + @Test + public void testGapMap() + { + SequenceI seq = new Sequence("test", "-A--B-CD-E--F-"); + seq.createDatasetSequence(); + assertEquals("[1, 4, 6, 7, 9, 12]", Arrays.toString(seq.gapMap())); + } } diff --git a/test/jalview/gui/JvSwingUtilsTest.java b/test/jalview/gui/JvSwingUtilsTest.java new file mode 100644 index 0000000..11e6ea5 --- /dev/null +++ b/test/jalview/gui/JvSwingUtilsTest.java @@ -0,0 +1,41 @@ +package jalview.gui; + +import static org.junit.Assert.assertEquals; + +import javax.swing.JScrollBar; + +import org.junit.Test; + +public class JvSwingUtilsTest +{ + + @Test + public void testGetScrollBarProportion() + { + /* + * orientation, value, extent (width), min, max + */ + JScrollBar sb = new JScrollBar(0, 125, 50, 0, 450); + + /* + * operating range is 25 - 425 (400 wide) so value 125 is 100/400ths of this + * range + */ + assertEquals(0.25f, JvSwingUtils.getScrollBarProportion(sb), 0.001f); + } + + @Test + public void testGetScrollValueForProportion() + { + /* + * orientation, value, extent (width), min, max + */ + JScrollBar sb = new JScrollBar(0, 125, 50, 0, 450); + + /* + * operating range is 25 - 425 (400 wide) so value 125 is a quarter of this + * range + */ + assertEquals(125, JvSwingUtils.getScrollValueForProportion(sb, 0.25f)); + } +} diff --git a/test/jalview/gui/PaintRefresherTest.java b/test/jalview/gui/PaintRefresherTest.java new file mode 100644 index 0000000..2737dd0 --- /dev/null +++ b/test/jalview/gui/PaintRefresherTest.java @@ -0,0 +1,115 @@ +package jalview.gui; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import jalview.datamodel.Alignment; +import jalview.datamodel.Sequence; +import jalview.datamodel.SequenceI; + +import java.awt.Component; +import java.util.List; +import java.util.Map; + +import javax.swing.JPanel; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class PaintRefresherTest +{ + // TODO would prefer PaintRefresher to be a single rather than static + @Before + public void setUp() + { + PaintRefresher.components.clear(); + } + + @After + public void tearDown() + { + PaintRefresher.components.clear(); + } + + @Test + public void testRegister() + { + JPanel jp = new JPanel(); + JPanel jp2 = new JPanel(); + JPanel jp3 = new JPanel(); + JPanel jp4 = new JPanel(); + PaintRefresher.Register(jp, "22"); + PaintRefresher.Register(jp, "22"); + PaintRefresher.Register(jp2, "22"); + PaintRefresher.Register(jp3, "33"); + PaintRefresher.Register(jp3, "44"); + PaintRefresher.Register(jp4, "44"); + + Map> registered = PaintRefresher.components; + assertEquals(3, registered.size()); + assertEquals(2, registered.get("22").size()); + assertEquals(1, registered.get("33").size()); + assertEquals(2, registered.get("44").size()); + assertTrue(registered.get("22").contains(jp)); + assertTrue(registered.get("22").contains(jp2)); + assertTrue(registered.get("33").contains(jp3)); + assertTrue(registered.get("44").contains(jp3)); + assertTrue(registered.get("44").contains(jp4)); + } + + @Test + public void testRemoveComponent() + { + Map> registered = PaintRefresher.components; + + // no error with an empty PaintRefresher + JPanel jp = new JPanel(); + JPanel jp2 = new JPanel(); + PaintRefresher.RemoveComponent(jp); + assertTrue(registered.isEmpty()); + + /* + * Add then remove one item + */ + PaintRefresher.Register(jp, "11"); + PaintRefresher.RemoveComponent(jp); + assertTrue(registered.isEmpty()); + + /* + * Add one item under two ids, then remove it. It is removed from both ids, + * and the now empty id is removed. + */ + PaintRefresher.Register(jp, "11"); + PaintRefresher.Register(jp, "22"); + PaintRefresher.Register(jp2, "22"); + PaintRefresher.RemoveComponent(jp); + // "11" is removed as now empty, only 22/jp2 left + assertEquals(1, registered.size()); + assertEquals(1, registered.get("22").size()); + assertTrue(registered.get("22").contains(jp2)); + } + + @Test + public void testGetAssociatedPanels() + { + SequenceI [] seqs = new SequenceI[]{new Sequence("", "ABC")}; + Alignment al = new Alignment(seqs); + + /* + * AlignFrame constructor has side-effects: AlignmentPanel is constructed, + * and SeqCanvas, IdPanel, AlignmentPanel are all registered under the + * sequence set id of the viewport. + */ + AlignViewport av = new AlignViewport(al); + AlignFrame af = new AlignFrame(al, 4, 1); + AlignmentPanel ap1 = af.alignPanel; + AlignmentPanel[] panels = PaintRefresher.getAssociatedPanels(av + .getSequenceSetId()); + assertEquals(1, panels.length); + assertSame(ap1, panels[0]); + + panels = PaintRefresher.getAssociatedPanels(av.getSequenceSetId() + 1); + assertEquals(0, panels.length); + } +} diff --git a/test/jalview/io/Jalview2xmlTests.java b/test/jalview/io/Jalview2xmlTests.java index 75626e9..b2fab71 100644 --- a/test/jalview/io/Jalview2xmlTests.java +++ b/test/jalview/io/Jalview2xmlTests.java @@ -239,13 +239,13 @@ public class Jalview2xmlTests @Test public void gatherViewsHere() throws Exception { - int origCount = Desktop.getAlignframes() == null ? 0 : Desktop - .getAlignframes().length; + int origCount = Desktop.getAlignFrames() == null ? 0 : Desktop + .getAlignFrames().length; AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded( "examples/exampleFile_2_7.jar", FormatAdapter.FILE); assertTrue("Didn't read in the example file correctly.", af != null); assertTrue("Didn't gather the views in the example file.", - Desktop.getAlignframes().length == 1 + origCount); + Desktop.getAlignFrames().length == 1 + origCount); } diff --git a/test/jalview/util/ComparisonTest.java b/test/jalview/util/ComparisonTest.java new file mode 100644 index 0000000..bfc2610 --- /dev/null +++ b/test/jalview/util/ComparisonTest.java @@ -0,0 +1,89 @@ +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"; + } +} diff --git a/test/jalview/util/MapListTest.java b/test/jalview/util/MapListTest.java new file mode 100644 index 0000000..1913a70 --- /dev/null +++ b/test/jalview/util/MapListTest.java @@ -0,0 +1,477 @@ +package jalview.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; + +public class MapListTest +{ + + @Test + public void testSomething() + { + MapList ml = new MapList(new int[] + { 1, 5, 10, 15, 25, 20 }, new int[] + { 51, 1 }, 1, 3); + MapList ml1 = new MapList(new int[] + { 1, 3, 17, 4 }, new int[] + { 51, 1 }, 1, 3); + MapList ml2 = new MapList(new int[] + { 1, 60 }, new int[] + { 1, 20 }, 3, 1); + // test internal consistency + int to[] = new int[51]; + testMap(ml, 1, 60); + MapList mldna = new MapList(new int[] + { 2, 2, 6, 8, 12, 16 }, new int[] + { 1, 3 }, 3, 1); + int[] frm = mldna.locateInFrom(1, 1); + testLocateFrom(mldna, 1, 1, new int[] + { 2, 2, 6, 7 }); + testMap(mldna, 1, 3); + /* + * for (int from=1; from<=51; from++) { int[] too=ml.shiftTo(from); int[] + * toofrom=ml.shiftFrom(too[0]); + * System.out.println("ShiftFrom("+from+")=="+too[0]+" % + * "+too[1]+"\t+-+\tShiftTo("+too[0]+")=="+toofrom[0]+" % "+toofrom[1]); } + */ + } + + private static void testLocateFrom(MapList mldna, int i, int j, int[] ks) + { + int[] frm = mldna.locateInFrom(i, j); + Assert.assertEquals("Failed test locate from " + i + " to " + j, + Arrays.toString(frm), Arrays.toString(ks)); + } + + /** + * test routine. not incremental. + * + * @param ml + * @param fromS + * @param fromE + */ + private void testMap(MapList ml, int fromS, int fromE) + { + // todo convert to JUnit style tests + for (int from = 1; from <= 25; from++) + { + int[] too = ml.shiftFrom(from); + System.out.print("ShiftFrom(" + from + ")=="); + if (too == null) + { + System.out.print("NaN\n"); + } + else + { + System.out.print(too[0] + " % " + too[1] + " (" + too[2] + ")"); + System.out.print("\t+--+\t"); + int[] toofrom = ml.shiftTo(too[0]); + if (toofrom != null) + { + if (toofrom[0] != from) + { + System.err.println("Mapping not reflexive:" + from + " " + + too[0] + "->" + toofrom[0]); + } + System.out.println("ShiftTo(" + too[0] + ")==" + toofrom[0] + + " % " + toofrom[1] + " (" + toofrom[2] + ")"); + } + else + { + System.out.println("ShiftTo(" + too[0] + ")==" + + "NaN! - not Bijective Mapping!"); + } + } + } + int mmap[][] = ml.makeFromMap(); + System.out.println("FromMap : (" + mmap[0][0] + " " + mmap[0][1] + " " + + mmap[0][2] + " " + mmap[0][3] + " "); + for (int i = 1; i <= mmap[1].length; i++) + { + if (mmap[1][i - 1] == -1) + { + System.out.print(i + "=XXX"); + + } + else + { + System.out.print(i + "=" + (mmap[0][2] + mmap[1][i - 1])); + } + if (i % 20 == 0) + { + System.out.print("\n"); + } + else + { + System.out.print(","); + } + } + // test range function + System.out.print("\nTest locateInFrom\n"); + { + int f = mmap[0][2], t = mmap[0][3]; + while (f <= t) + { + System.out.println("Range " + f + " to " + t); + int rng[] = ml.locateInFrom(f, t); + if (rng != null) + { + for (int i = 0; i < rng.length; i++) + { + System.out.print(rng[i] + ((i % 2 == 0) ? "," : ";")); + } + } + else + { + System.out.println("No range!"); + } + System.out.print("\nReversed\n"); + rng = ml.locateInFrom(t, f); + if (rng != null) + { + for (int i = 0; i < rng.length; i++) + { + System.out.print(rng[i] + ((i % 2 == 0) ? "," : ";")); + } + } + else + { + System.out.println("No range!"); + } + System.out.print("\n"); + f++; + t--; + } + } + System.out.print("\n"); + mmap = ml.makeToMap(); + System.out.println("ToMap : (" + mmap[0][0] + " " + mmap[0][1] + " " + + mmap[0][2] + " " + mmap[0][3] + " "); + for (int i = 1; i <= mmap[1].length; i++) + { + if (mmap[1][i - 1] == -1) + { + System.out.print(i + "=XXX"); + + } + else + { + System.out.print(i + "=" + (mmap[0][2] + mmap[1][i - 1])); + } + if (i % 20 == 0) + { + System.out.print("\n"); + } + else + { + System.out.print(","); + } + } + System.out.print("\n"); + // test range function + System.out.print("\nTest locateInTo\n"); + { + int f = mmap[0][2], t = mmap[0][3]; + while (f <= t) + { + System.out.println("Range " + f + " to " + t); + int rng[] = ml.locateInTo(f, t); + if (rng != null) + { + for (int i = 0; i < rng.length; i++) + { + System.out.print(rng[i] + ((i % 2 == 0) ? "," : ";")); + } + } + else + { + System.out.println("No range!"); + } + System.out.print("\nReversed\n"); + rng = ml.locateInTo(t, f); + if (rng != null) + { + for (int i = 0; i < rng.length; i++) + { + System.out.print(rng[i] + ((i % 2 == 0) ? "," : ";")); + } + } + else + { + System.out.println("No range!"); + } + f++; + t--; + System.out.print("\n"); + } + } + } + + /** + * Tests for method that locates ranges in the 'from' map for given range in + * the 'to' map. + */ + @Test + public void testLocateInFrom_noIntrons() + { + /* + * Simple mapping with no introns + */ + int[] codons = new int[] + { 1, 12 }; + int[] protein = new int[] + { 1, 4 }; + MapList ml = new MapList(codons, protein, 3, 1); + assertEquals("[1, 3]", Arrays.toString(ml.locateInFrom(1, 1))); + assertEquals("[4, 6]", Arrays.toString(ml.locateInFrom(2, 2))); + assertEquals("[7, 9]", Arrays.toString(ml.locateInFrom(3, 3))); + assertEquals("[10, 12]", Arrays.toString(ml.locateInFrom(4, 4))); + assertEquals("[1, 6]", Arrays.toString(ml.locateInFrom(1, 2))); + assertEquals("[1, 9]", Arrays.toString(ml.locateInFrom(1, 3))); + assertEquals("[1, 12]", Arrays.toString(ml.locateInFrom(1, 4))); + assertEquals("[4, 9]", Arrays.toString(ml.locateInFrom(2, 3))); + assertEquals("[4, 12]", Arrays.toString(ml.locateInFrom(2, 4))); + assertEquals("[7, 12]", Arrays.toString(ml.locateInFrom(3, 4))); + assertEquals("[10, 12]", Arrays.toString(ml.locateInFrom(4, 4))); + + assertNull(ml.locateInFrom(0, 0)); + assertNull(ml.locateInFrom(1, 5)); + assertNull(ml.locateInFrom(-1, 1)); + } + + /** + * Tests for method that locates ranges in the 'from' map for given range in + * the 'to' map. + */ + @Test + public void testLocateInFrom_withIntrons() + { + /* + * Exons at positions [2, 3, 5] [6, 7, 9] [10, 12, 14] [16, 17, 18] i.e. + * 2-3, 5-7, 9-10, 12-12, 14-14, 16-18 + */ + int[] codons = + { 2, 3, 5, 7, 9, 10, 12, 12, 14, 14, 16, 18 }; + int[] protein = + { 1, 4 }; + MapList ml = new MapList(codons, protein, 3, 1); + assertEquals("[2, 3, 5, 5]", Arrays.toString(ml.locateInFrom(1, 1))); + assertEquals("[6, 7, 9, 9]", Arrays.toString(ml.locateInFrom(2, 2))); + assertEquals("[10, 10, 12, 12, 14, 14]", + Arrays.toString(ml.locateInFrom(3, 3))); + assertEquals("[16, 18]", Arrays.toString(ml.locateInFrom(4, 4))); + } + + /** + * Tests for method that locates ranges in the 'to' map for given range in the + * 'from' map. + */ + @Test + public void testLocateInTo_noIntrons() + { + /* + * Simple mapping with no introns + */ + int[] codons = new int[] + { 1, 12 }; + int[] protein = new int[] + { 1, 4 }; + MapList ml = new MapList(codons, protein, 3, 1); + assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(1, 3))); + assertEquals("[2, 2]", Arrays.toString(ml.locateInTo(4, 6))); + assertEquals("[3, 3]", Arrays.toString(ml.locateInTo(7, 9))); + assertEquals("[4, 4]", Arrays.toString(ml.locateInTo(10, 12))); + assertEquals("[1, 2]", Arrays.toString(ml.locateInTo(1, 6))); + assertEquals("[1, 3]", Arrays.toString(ml.locateInTo(1, 9))); + assertEquals("[1, 4]", Arrays.toString(ml.locateInTo(1, 12))); + assertEquals("[2, 2]", Arrays.toString(ml.locateInTo(4, 6))); + assertEquals("[2, 4]", Arrays.toString(ml.locateInTo(4, 12))); + + /* + * A part codon is treated as if a whole one. + */ + assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(1, 1))); + assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(1, 2))); + assertEquals("[1, 2]", Arrays.toString(ml.locateInTo(1, 4))); + assertEquals("[1, 3]", Arrays.toString(ml.locateInTo(2, 8))); + assertEquals("[1, 4]", Arrays.toString(ml.locateInTo(3, 11))); + assertEquals("[2, 4]", Arrays.toString(ml.locateInTo(5, 11))); + + assertNull(ml.locateInTo(0, 0)); + assertNull(ml.locateInTo(1, 13)); + assertNull(ml.locateInTo(-1, 1)); + } + + /** + * Tests for method that locates ranges in the 'to' map for given range in the + * 'from' map. + */ + @Test + public void testLocateInTo_withIntrons() + { + /* + * Exons at positions [2, 3, 5] [6, 7, 9] [10, 12, 14] [16, 17, 18] i.e. + * 2-3, 5-7, 9-10, 12-12, 14-14, 16-18 + */ + int[] codons = + { 2, 3, 5, 7, 9, 10, 12, 12, 14, 14, 16, 18 }; + /* + * Mapped proteins at positions 1, 3, 4, 6 in the sequence + */ + int[] protein = + { 1, 1, 3, 4, 6, 6 }; + MapList ml = new MapList(codons, protein, 3, 1); + + /* + * Can't map from an unmapped position + */ + assertNull(ml.locateInTo(1, 2)); + assertNull(ml.locateInTo(2, 4)); + assertNull(ml.locateInTo(4, 4)); + + /* + * Valid range or subrange of codon1 maps to protein1. + */ + assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(2, 2))); + assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(3, 3))); + assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(3, 5))); + assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(2, 3))); + assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(2, 5))); + + // codon position 6 starts the next protein: + assertEquals("[1, 1, 3, 3]", Arrays.toString(ml.locateInTo(3, 6))); + + // codon positions 7 to 17 (part) cover proteins 2/3/4 at positions 3/4/6 + assertEquals("[3, 4, 6, 6]", Arrays.toString(ml.locateInTo(7, 17))); + + } + + /** + * Test equals method. + */ + @Test + public void testEquals() + { + int[] codons = new int[] + { 2, 3, 5, 7, 9, 10, 12, 12, 14, 14, 16, 18 }; + int[] protein = new int[] + { 1, 4 }; + MapList ml = new MapList(codons, protein, 3, 1); + MapList ml1 = new MapList(codons, protein, 3, 1); // same values + MapList ml2 = new MapList(codons, protein, 2, 1); // fromRatio differs + MapList ml3 = new MapList(codons, protein, 3, 2); // toRatio differs + codons[2] = 4; + MapList ml6 = new MapList(codons, protein, 3, 1); // fromShifts differ + protein[1] = 3; + MapList ml7 = new MapList(codons, protein, 3, 1); // toShifts differ + + assertTrue(ml.equals(ml)); + assertTrue(ml.equals(ml1)); + assertTrue(ml1.equals(ml)); + + assertFalse(ml.equals(null)); + assertFalse(ml.equals("hello")); + assertFalse(ml.equals(ml2)); + assertFalse(ml.equals(ml3)); + assertFalse(ml.equals(ml6)); + assertFalse(ml.equals(ml7)); + assertFalse(ml6.equals(ml7)); + + try + { + MapList ml4 = new MapList(codons, null, 3, 1); // toShifts null + assertFalse(ml.equals(ml4)); + } catch (NullPointerException e) + { + // actually thrown by constructor before equals can be called + } + try + { + MapList ml5 = new MapList(null, protein, 3, 1); // fromShifts null + assertFalse(ml.equals(ml5)); + } catch (NullPointerException e) + { + // actually thrown by constructor before equals can be called + } + } + + /** + * Test for the method that flattens a list of ranges into a single array. + */ + @Test + public void testGetRanges() + { + List ranges = new ArrayList(); + ranges.add(new int[] + { 2, 3 }); + ranges.add(new int[] + { 5, 6 }); + assertEquals("[2, 3, 5, 6]", Arrays.toString(MapList.getRanges(ranges))); + } + + /** + * Check state after construction + */ + @Test + public void testConstructor() + { + int[] codons = + { 2, 3, 5, 7, 9, 10, 12, 12, 14, 14, 16, 18 }; + int[] protein = + { 1, 1, 3, 4, 6, 6 }; + MapList ml = new MapList(codons, protein, 3, 1); + assertEquals(3, ml.getFromRatio()); + assertEquals(2, ml.getFromLowest()); + assertEquals(18, ml.getFromHighest()); + assertEquals(1, ml.getToLowest()); + assertEquals(6, ml.getToHighest()); + assertEquals("[2, 3, 5, 7, 9, 10, 12, 12, 14, 14, 16, 18]", + Arrays.toString(ml.getFromRanges())); + assertEquals("[1, 1, 3, 4, 6, 6]", Arrays.toString(ml.getToRanges())); + + /* + * Also copy constructor + */ + MapList ml2 = new MapList(ml); + assertEquals(3, ml2.getFromRatio()); + assertEquals(2, ml2.getFromLowest()); + assertEquals(18, ml2.getFromHighest()); + assertEquals(1, ml2.getToLowest()); + assertEquals(6, ml2.getToHighest()); + assertEquals("[2, 3, 5, 7, 9, 10, 12, 12, 14, 14, 16, 18]", + Arrays.toString(ml2.getFromRanges())); + assertEquals("[1, 1, 3, 4, 6, 6]", Arrays.toString(ml2.getToRanges())); + } + + /** + * Test the method that creates an inverse mapping + */ + @Test + public void testGetInverse() + { + int[] codons = + { 2, 3, 5, 7, 9, 10, 12, 12, 14, 14, 16, 18 }; + int[] protein = + { 1, 1, 3, 4, 6, 6 }; + + MapList ml = new MapList(codons, protein, 3, 1); + MapList ml2 = ml.getInverse(); + assertEquals(ml.getFromRatio(), ml2.getToRatio()); + assertEquals(ml.getFromRatio(), ml2.getToRatio()); + assertEquals(ml.getToHighest(), ml2.getFromHighest()); + assertEquals(ml.getFromHighest(), ml2.getToHighest()); + assertEquals(Arrays.toString(ml.getFromRanges()), + Arrays.toString(ml2.getToRanges())); + assertEquals(Arrays.toString(ml.getToRanges()), + Arrays.toString(ml2.getFromRanges())); + } +} diff --git a/test/jalview/util/ShiftListTest.java b/test/jalview/util/ShiftListTest.java new file mode 100644 index 0000000..f680d6c --- /dev/null +++ b/test/jalview/util/ShiftListTest.java @@ -0,0 +1,35 @@ +package jalview.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; + +public class ShiftListTest +{ + + @Test + public void testParseMap() + { + assertNull(ShiftList.parseMap(null)); + assertNull(ShiftList.parseMap(new int[] + {})); + + /* + * Gap map showing residues in aligned positions 2,3,6,8,9,10,12 + */ + int[] gm = new int[] + { 2, 3, 6, 8, 9, 10, 12 }; + List shifts = ShiftList.parseMap(gm).getShifts(); + assertEquals(4, shifts.size()); + + // TODO are these results (which pass) correct?? + assertEquals("[0, 2]", Arrays.toString(shifts.get(0))); + assertEquals("[4, 2]", Arrays.toString(shifts.get(1))); + assertEquals("[7, 1]", Arrays.toString(shifts.get(2))); + assertEquals("[11, 1]", Arrays.toString(shifts.get(3))); + } +} diff --git a/test/jalview/util/StringUtilsTest.java b/test/jalview/util/StringUtilsTest.java new file mode 100644 index 0000000..22a4130 --- /dev/null +++ b/test/jalview/util/StringUtilsTest.java @@ -0,0 +1,71 @@ +package jalview.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +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))); + } + + @Test + public void testGetLastToken() + { + assertNull(StringUtils.getLastToken(null, null)); + assertNull(StringUtils.getLastToken(null, "/")); + assertEquals("a", StringUtils.getLastToken("a", null)); + + assertEquals("abc", StringUtils.getLastToken("abc", "/")); + assertEquals("c", StringUtils.getLastToken("abc", "b")); + assertEquals("file1.dat", StringUtils.getLastToken( + "file://localhost:8080/data/examples/file1.dat", "/")); + } +}