From 95ceead3cb4ea5aa5260e1fd4ab53658b64866e1 Mon Sep 17 00:00:00 2001 From: gmungoc Date: Fri, 30 Jan 2015 17:19:01 +0000 Subject: [PATCH] JAL-845 linked protein/dna 'slave' further PoC functionality --- resources/lang/Messages.properties | 1 + src/jalview/analysis/AlignmentSorter.java | 83 ++--- src/jalview/analysis/AlignmentUtils.java | 133 +++++-- src/jalview/api/AlignViewportI.java | 19 +- src/jalview/bin/Jalview.java | 2 +- src/jalview/commands/EditCommand.java | 56 ++- src/jalview/commands/OrderCommand.java | 42 ++- src/jalview/datamodel/Alignment.java | 32 +- src/jalview/datamodel/AlignmentOrder.java | 43 +-- src/jalview/datamodel/SequenceGroup.java | 19 +- src/jalview/gui/AlignFrame.java | 46 ++- src/jalview/gui/AlignViewport.java | 116 +++--- src/jalview/gui/Desktop.java | 117 +++--- src/jalview/gui/IdCanvas.java | 16 +- src/jalview/gui/Jalview2XML.java | 7 +- src/jalview/gui/PopupMenu.java | 10 +- src/jalview/gui/SeqPanel.java | 146 ++++++-- src/jalview/gui/SplitFrame.java | 49 +++ src/jalview/gui/TreeCanvas.java | 103 +++++- src/jalview/gui/TreePanel.java | 58 ++- src/jalview/gui/WsJobParameters.java | 2 +- src/jalview/io/VamsasAppDatastore.java | 2 +- src/jalview/jbgui/GSplitFrame.java | 43 +++ src/jalview/jbgui/GTreePanel.java | 21 +- src/jalview/structure/CommandListener.java | 4 +- .../structure/StructureSelectionManager.java | 345 +++++------------- src/jalview/util/MappingUtils.java | 381 ++++++++++++++++++++ src/jalview/viewmodel/AlignmentViewport.java | 50 +-- test/jalview/analysis/AlignmentUtilsTests.java | 150 ++++---- test/jalview/commands/EditCommandTest.java | 15 +- test/jalview/io/Jalview2xmlTests.java | 6 +- 31 files changed, 1412 insertions(+), 705 deletions(-) create mode 100644 src/jalview/gui/SplitFrame.java create mode 100644 src/jalview/jbgui/GSplitFrame.java create mode 100644 src/jalview/util/MappingUtils.java diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties index 8cf8797..0d67dac 100644 --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@ -745,6 +745,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 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 2dbe015..f3e126d 100644 --- a/src/jalview/analysis/AlignmentUtils.java +++ b/src/jalview/analysis/AlignmentUtils.java @@ -417,7 +417,9 @@ public class AlignmentUtils /** * Align sequence 'alignTo' the same way as 'alignFrom', using the mapping to - * match residues and codons. + * 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 @@ -448,12 +450,13 @@ public class AlignmentUtils /* * Traverse the aligned protein sequence. */ - int sourceGapLength = 0; + int sourceGapMappedLength = 0; + boolean inExon = false; for (char sourceChar : thatAligned) { if (sourceChar == sourceGap) { - sourceGapLength++; + sourceGapMappedLength += ratio; continue; } @@ -476,7 +479,7 @@ public class AlignmentUtils int mappedCodonStart = mappedPos[0]; // position (1...) of codon start int mappedCodonEnd = mappedPos[mappedPos.length - 1]; // codon end pos - int trailingCopiedGapLength = 0; + StringBuilder trailingCopiedGap = new StringBuilder(); /* * Copy dna sequence up to and including this codon. Optionally, include @@ -486,7 +489,7 @@ public class AlignmentUtils * Note this only works for 'linear' splicing, not reverse or interleaved. * But then 'align dna as protein' doesn't make much sense otherwise. */ - boolean inCodon = false; + int intronLength = 0; while (basesWritten < mappedCodonEnd && thisSeqPos < thisSeq.length) { final char c = thisSeq[thisSeqPos++]; @@ -494,40 +497,49 @@ public class AlignmentUtils { basesWritten++; - /* - * Is this the start of the mapped codon? If so, add in any extra gap - * due to the protein alignment. - */ - if (basesWritten == mappedCodonStart) + if (basesWritten < mappedCodonStart) { - inCodon = true; - int gapsToAdd = Math.max(0, ratio * sourceGapLength - - trailingCopiedGapLength); + /* + * 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); } - sourceGapLength = 0; + sourceGapMappedLength = 0; + inExon = true; } thisAligned.append(c); - trailingCopiedGapLength = 0; + trailingCopiedGap = new StringBuilder(); } - else if ((!inCodon && preserveUnmappedGaps) - || (inCodon && preserveMappedGaps)) + else { - thisAligned.append(c); - trailingCopiedGapLength++; + if (inExon && preserveMappedGaps) + { + trailingCopiedGap.append(myGapChar); + } + else if (!inExon && preserveUnmappedGaps) + { + trailingCopiedGap.append(myGapChar); + } } } - - /* - * Expand (if necessary) the trailing gap to the size of the aligned gap. - */ - int gapsToAdd = (ratio * sourceGapLength - trailingCopiedGapLength); - for (int i = 0; i < gapsToAdd; i++) - { - thisAligned.append(myGapChar); - } } /* @@ -548,4 +560,69 @@ public class AlignmentUtils */ 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/api/AlignViewportI.java b/src/jalview/api/AlignViewportI.java index d8ba30d..6753a29 100644 --- a/src/jalview/api/AlignViewportI.java +++ b/src/jalview/api/AlignViewportI.java @@ -20,11 +20,6 @@ */ package jalview.api; -import java.awt.Color; -import java.util.Hashtable; -import java.util.List; -import java.util.Map; - import jalview.analysis.Conservation; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; @@ -36,6 +31,11 @@ import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; import jalview.schemes.ColourSchemeI; +import java.awt.Color; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; + /** * @author jimp * @@ -200,4 +200,13 @@ public interface AlignViewportI List getVisibleAlignmentAnnotation( boolean selectedOnly); + /** + * Returns a viewport which is a 'slave of' this one i.e. updated in + * tandem in some sense, or null if none is set. + * + * @return + */ + AlignViewportI getSlave(); + + void setSlave(AlignViewportI sl); } diff --git a/src/jalview/bin/Jalview.java b/src/jalview/bin/Jalview.java index ff1b180..d11a8f9 100755 --- a/src/jalview/bin/Jalview.java +++ b/src/jalview/bin/Jalview.java @@ -924,7 +924,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/commands/EditCommand.java b/src/jalview/commands/EditCommand.java index ba01b1c..38a45ce 100644 --- a/src/jalview/commands/EditCommand.java +++ b/src/jalview/commands/EditCommand.java @@ -63,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(); @@ -551,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; @@ -1227,7 +1275,9 @@ public class EditCommand implements CommandI } else { - throw new IllegalStateException("Can't undo edit action " + action); + System.err.println("Can't undo edit action " + action); + // throw new IllegalStateException("Can't undo edit action " + + // action); } } } 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/Alignment.java b/src/jalview/datamodel/Alignment.java index cea5956..5c2bc22 100755 --- a/src/jalview/datamodel/Alignment.java +++ b/src/jalview/datamodel/Alignment.java @@ -90,6 +90,20 @@ 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]); + } + initAlignment(seqs); + } + + /** * Make an alignment from an array of Sequences. * * @param sequences @@ -1610,10 +1624,17 @@ public class Alignment implements AlignmentI return dataset; } + /** + * Align this alignment like the given (mapped) one. + */ @Override public int alignAs(AlignmentI al) { - return alignAs(al, true, true); + /* + * Currently retains unmapped gaps (in introns), regaps mapped regions + * (exons) + */ + return alignAs(al, false, true); } /** @@ -1625,7 +1646,16 @@ public class Alignment implements AlignmentI * 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, 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/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/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index 84872ca..b47e7d1 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -27,11 +27,11 @@ import jalview.analysis.AlignmentUtils.MappingResult; import jalview.analysis.Conservation; import jalview.analysis.CrossRef; import jalview.analysis.Dna; -import jalview.analysis.NJTree; 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; @@ -162,7 +162,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, public AlignViewControllerI avc; - Vector alignPanels = new Vector(); + List alignPanels = new ArrayList(); /** * Last format used to load or save alignments in this window @@ -599,7 +599,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()); @@ -642,7 +642,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); } @@ -727,7 +727,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, int alreadyLinkedCount = 0; final AlignmentI thisAlignment = this.alignPanel.getAlignment(); - for (AlignFrame af : Desktop.getAlignframes()) + for (AlignFrame af : Desktop.getAlignFrames()) { if (af.alignPanel != null) { @@ -780,7 +780,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, int seqCount = 0; int alignCount = 0; final AlignmentI thisAlignment = this.alignPanel.getAlignment(); - for (AlignFrame af : Desktop.getAlignframes()) + for (AlignFrame af : Desktop.getAlignFrames()) { if (af.alignPanel != null) { @@ -1441,7 +1441,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(); } } @@ -1471,7 +1471,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) // { @@ -1551,11 +1551,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; } @@ -1705,6 +1705,21 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, viewport.getAlignment().moveSelectedSequencesByOne(sg, viewport.getHiddenRepSequences(), up); alignPanel.paintAlignment(true); + + final AlignViewportI slave = viewport.getSlave(); + if (slave != null) + { + final SequenceGroup selectionGroup = viewport.getSlave() + .getSelectionGroup(); + if (selectionGroup != null) + { + viewport.getSlave() + .getAlignment() + .moveSelectedSequencesByOne( + viewport.getSlave().getSelectionGroup(), + viewport.getSlave().getHiddenRepSequences(), up); + } + } } synchronized void slideSequences(boolean right, int size) @@ -2174,7 +2189,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); } @@ -4227,13 +4242,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { 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)); } @@ -5329,7 +5343,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { if (index > -1) { - alignPanel = (AlignmentPanel) alignPanels.elementAt(index); + alignPanel = alignPanels.get(index); viewport = alignPanel.av; avc.setViewportAndAlignmentPanel(viewport, alignPanel); setMenusFromViewport(viewport); @@ -5878,7 +5892,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @return alignment panels in this alignment frame */ - public List getAlignPanels() + public List getAlignPanels() { return alignPanels == null ? Arrays.asList(alignPanel) : alignPanels; diff --git a/src/jalview/gui/AlignViewport.java b/src/jalview/gui/AlignViewport.java index 705f53a..f00e4d3 100644 --- a/src/jalview/gui/AlignViewport.java +++ b/src/jalview/gui/AlignViewport.java @@ -45,7 +45,7 @@ import jalview.analysis.NJTree; import jalview.api.AlignViewportI; import jalview.bin.Cache; import jalview.commands.CommandI; -import jalview.commands.EditCommand; +import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentI; import jalview.datamodel.ColumnSelection; import jalview.datamodel.PDBEntry; @@ -70,13 +70,11 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.Hashtable; -import java.util.List; import java.util.Set; import java.util.Vector; import javax.swing.JInternalFrame; import javax.swing.JOptionPane; -import javax.swing.JSplitPane; /** * DOCUMENT ME! @@ -1115,6 +1113,9 @@ public class AlignViewport extends AlignmentViewport implements boolean showSeqFeaturesHeight; + /** + * Send the current selection to be broadcast to any selection listeners. + */ public void sendSelection() { jalview.structure.StructureSelectionManager @@ -1325,23 +1326,30 @@ public class AlignViewport extends AlignmentViewport implements */ @Override public void mirrorCommand(CommandI command, boolean undo, - StructureSelectionManager ssm) + StructureSelectionManager ssm, VamsasSource source) { /* - * Only EditCommand is currently handled by listeners. + * ...work in progress... do nothing unless we are a 'slave' of the source + * May replace this with direct calls not via SSM. */ - if (!(command instanceof EditCommand)) + if (source instanceof AlignViewportI + && ((AlignViewportI) source).getSlave() == this) + { + // ok to continue; + } + else { return; } - EditCommand edit = (EditCommand) command; - List seqs = getAlignment().getSequences(); - EditCommand mappedCommand = ssm.mapEditCommand(edit, undo, seqs, + CommandI mappedCommand = ssm.mapCommand(command, undo, getAlignment(), getGapCharacter()); - AlignmentI[] views = getAlignPanel().alignFrame.getViewAlignments(); - mappedCommand.performEdit(0, views); - getAlignPanel().alignmentChanged(); + if (mappedCommand != null) + { + AlignmentI[] views = getAlignPanel().alignFrame.getViewAlignments(); + mappedCommand.doCommand(views); + getAlignPanel().alignmentChanged(); + } } @Override @@ -1416,8 +1424,8 @@ public class AlignViewport extends AlignmentViewport implements /** * Add the sequences from the given alignment to this viewport. Optionally, - * may give the user the option to open a new frame or panel linking cDNA and - * protein. + * may give the user the option to open a new frame, or split panel, with cDNA + * and protein linked. * * @param al * @param title @@ -1459,9 +1467,8 @@ public class AlignViewport extends AlignmentViewport implements /** * 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 - - * either because the user declined the offer, or because no mapping could be - * made. + * alignment. Returns true if the new alignment was opened, false if not, + * because the user declined the offer. * * @param title */ @@ -1477,11 +1484,6 @@ public class AlignViewport extends AlignmentViewport implements MessageManager.getString("label.open_linked_alignment"), JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[0]); - // int reply = JOptionPane.showInternalConfirmDialog(Desktop.desktop, - // question, - // MessageManager.getString("label.open_linked_alignment"), - // JOptionPane.YES_NO_OPTION, - // JOptionPane.QUESTION_MESSAGE); if (response != 1 && response != 2) { @@ -1494,13 +1496,19 @@ public class AlignViewport extends AlignmentViewport implements * Create the AlignFrame first (which creates the new alignment's datasets), * before attempting sequence mapping. */ - AlignFrame alignFrame = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH, + AlignFrame newAlignFrame = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); - final AlignmentI protein = al.isNucleotide() ? getAlignment() : al; - final AlignmentI cdna = al.isNucleotide() ? al : getAlignment(); + /* + * 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(); + final AlignmentI protein = al.isNucleotide() ? thisAlignment : al; + final AlignmentI cdna = al.isNucleotide() ? al : thisAlignment; - alignFrame.statusBar.setText(MessageManager.formatMessage( + newAlignFrame.statusBar.setText(MessageManager.formatMessage( "label.successfully_loaded_file", new Object[] { title })); @@ -1510,12 +1518,11 @@ public class AlignViewport extends AlignmentViewport implements // { // alignFrame.setFileName(file, format); // } + if (openInNewWindow) { - /* - * open in new window - */ - Desktop.addInternalFrame(alignFrame, title, AlignFrame.DEFAULT_WIDTH, + Desktop.addInternalFrame(newAlignFrame, title, + AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); } @@ -1523,6 +1530,8 @@ public class AlignViewport extends AlignmentViewport implements * Try to find mappings for at least one sequence. */ MappingResult mapped = AlignmentUtils.mapProteinToCdna(protein, cdna); + final StructureSelectionManager ssm = StructureSelectionManager + .getStructureSelectionManager(Desktop.instance); if (mapped == MappingResult.Mapped) { @@ -1530,15 +1539,7 @@ public class AlignViewport extends AlignmentViewport implements * Register the mappings (held on the protein alignment) with the * StructureSelectionManager (for mouseover linking). */ - final StructureSelectionManager ssm = StructureSelectionManager - .getStructureSelectionManager(Desktop.instance); ssm.addMappings(protein.getCodonFrames()); - - /* - * Set the cDNA to listen for edits on the protein. - */ - ssm.addCommandListener(al.isNucleotide() ? alignFrame.getViewport() - : this); } else { @@ -1555,7 +1556,8 @@ public class AlignViewport extends AlignmentViewport implements try { - alignFrame.setMaximum(jalview.bin.Cache.getDefault("SHOW_FULLSCREEN", + newAlignFrame.setMaximum(jalview.bin.Cache.getDefault( + "SHOW_FULLSCREEN", false)); } catch (java.beans.PropertyVetoException ex) { @@ -1564,22 +1566,32 @@ public class AlignViewport extends AlignmentViewport implements if (openSplitPane) { /* - * Open in split pane. Original sequence above, new one below. + * Open in split pane. DNA sequence above, protein below. */ - JInternalFrame splitFrame = new JInternalFrame(); - splitFrame.setSize(AlignFrame.DEFAULT_WIDTH, - AlignFrame.DEFAULT_HEIGHT); - // TODO not quite right to 'move' AlignPanel from 'this' to the split - // pane - // TODO probably want linked editing set up here - JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, - getAlignPanel(), alignFrame.alignPanel); - splitPane.setDividerLocation(0.5d); - splitFrame.setSize(AlignFrame.DEFAULT_WIDTH, - AlignFrame.DEFAULT_HEIGHT); - splitFrame.add(splitPane); + AlignFrame copyMe = new AlignFrame(thisAlignment, + AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); + copyMe.setTitle(""); // TODO would like this AlignFrame.title here + final AlignFrame proteinFrame = al.isNucleotide() ? copyMe + : newAlignFrame; + final AlignFrame cdnaFrame = al.isNucleotide() ? newAlignFrame + : copyMe; + newAlignFrame.setTitle(title); + + cdnaFrame.setVisible(true); + proteinFrame.setVisible(true); + JInternalFrame splitFrame = new SplitFrame(cdnaFrame, proteinFrame); Desktop.addInternalFrame(splitFrame, title, AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); + + /* + * Set the cDNA to list for edits on the protein. + */ + ssm.addCommandListener(cdnaFrame.getViewport()); + + /* + * cDNA is 'slaved' to edits, selection, sorting, show/hide on protein + */ + proteinFrame.getViewport().setSlave(cdnaFrame.getViewport()); } return true; diff --git a/src/jalview/gui/Desktop.java b/src/jalview/gui/Desktop.java index 6eeb0c2..e2a57af 100644 --- a/src/jalview/gui/Desktop.java +++ b/src/jalview/gui/Desktop.java @@ -68,6 +68,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; @@ -1638,39 +1639,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) + { + return null; + } + for (AlignFrame af : frames) { - if (frames[t] instanceof AlignFrame) + 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; } @@ -1699,11 +1702,10 @@ public class Desktop extends jalview.jbgui.GDesktop implements { for (int a = 0; a < afr.alignPanels.size(); a++) { - if (sequenceSetId.equals(((AlignmentPanel) afr.alignPanels - .elementAt(a)).av.getSequenceSetId())) + if (sequenceSetId.equals(afr.alignPanels.get(a).av + .getSequenceSetId())) { - viewp.addElement(((AlignmentPanel) afr.alignPanels - .elementAt(a)).av); + viewp.addElement(afr.alignPanels.get(a).av); } } } @@ -1734,7 +1736,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())) @@ -1768,7 +1770,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; @@ -2277,7 +2279,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements /** * Accessor method to quickly get all the AlignmentFrames loaded. */ - public static AlignFrame[] getAlignframes() + public static AlignFrame[] getAlignFrames() { JInternalFrame[] frames = Desktop.desktop.getAllFrames(); @@ -2285,35 +2287,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 AppJmol[] getJmols() { JInternalFrame[] frames = Desktop.desktop.getAllFrames(); @@ -2322,32 +2332,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) - { - AppJmol af = (AppJmol) frames[i]; - avp.addElement(af); - } + AppJmol af = (AppJmol) frames[i]; + avp.add(af); } - } catch (Exception ex) - { - ex.printStackTrace(); } if (avp.size() == 0) { return null; } - AppJmol afs[] = new AppJmol[avp.size()]; - for (int i = 0, j = avp.size(); i < j; i++) - { - afs[i] = (AppJmol) avp.elementAt(i); - } - avp.clear(); + AppJmol afs[] = avp.toArray(new AppJmol[avp.size()]); return afs; } @@ -2471,7 +2470,7 @@ 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 diff --git a/src/jalview/gui/IdCanvas.java b/src/jalview/gui/IdCanvas.java index 91b23e4..bf2415e 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/Jalview2XML.java b/src/jalview/gui/Jalview2XML.java index be89829..0e3d878 100644 --- a/src/jalview/gui/Jalview2XML.java +++ b/src/jalview/gui/Jalview2XML.java @@ -402,8 +402,7 @@ public class Jalview2XML for (ap = 0; ap < apSize; ap++) { - AlignmentPanel apanel = (AlignmentPanel) af.alignPanels - .elementAt(ap); + AlignmentPanel apanel = af.alignPanels.get(ap); String fileName = apSize == 1 ? shortName : ap + shortName; if (!fileName.endsWith(".xml")) { @@ -458,8 +457,8 @@ public class Jalview2XML Hashtable dsses = new Hashtable(); for (ap = 0; ap < apSize; ap++) { - AlignmentPanel apanel = (AlignmentPanel) af.alignPanels - .elementAt(ap); + AlignmentPanel apanel = af.alignPanels + .get(ap); String jfileName = apSize == 1 ? fileName : fileName + ap; if (!jfileName.endsWith(".xml")) { diff --git a/src/jalview/gui/PopupMenu.java b/src/jalview/gui/PopupMenu.java index c2bee83..1778f27 100644 --- a/src/jalview/gui/PopupMenu.java +++ b/src/jalview/gui/PopupMenu.java @@ -2474,15 +2474,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/SeqPanel.java b/src/jalview/gui/SeqPanel.java index e923be7..cca1685 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -20,9 +20,11 @@ */ package jalview.gui; +import jalview.api.AlignViewportI; import jalview.commands.EditCommand; import jalview.commands.EditCommand.Action; import jalview.commands.EditCommand.Edit; +import jalview.datamodel.AlignedCodonFrame; import jalview.datamodel.ColumnSelection; import jalview.datamodel.SearchResults; import jalview.datamodel.Sequence; @@ -1869,52 +1871,67 @@ 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)) + boolean iSentTheSelection = (av == source || (source instanceof AlignViewport && ((AlignViewport) source) - .getSequenceSetId().equals(av.getSequenceSetId()))) + .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 'slave'. + */ + if (selectionAsSlave(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) - { - 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; - } - } - if (sgroup != null && sgroup.getSize() > 0) + if (av.getAlignment() == null) { - av.setSelectionGroup(sgroup); + jalview.bin.Cache.log.warn("alignviewport av SeqSetId=" + + av.getSequenceSetId() + " ViewId=" + av.getViewId() + + " 's alignment is NULL! returning immediately."); + return; } - else + sgroup = seqsel.intersect(av.getAlignment(), + (av.hasHiddenRows()) ? av.getHiddenRepSequences() : null); + if ((sgroup == null || sgroup.getSize() == 0) + || (colsel == null || colsel.size() == 0)) { - av.setSelectionGroup(null); + // don't copy columns if the region didn't intersect. + copycolsel = false; } - 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 @@ -1942,6 +1959,7 @@ public class SeqPanel extends JPanel implements MouseListener, av.isColSelChanged(true); repaint = true; } + if (copycolsel && av.hasHiddenColumns() && (av.getColumnSelection() == null || av.getColumnSelection() @@ -1949,11 +1967,73 @@ 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 'slave' 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 selectionAsSlave(SequenceGroup seqsel, + ColumnSelection colsel, SelectionSource source) + { + if (!(source instanceof AlignViewportI)) { + return false; + } + final AlignViewportI sourceAv = (AlignViewportI) source; + if (sourceAv.getSlave() != av) + { + return false; + } + + /* + * Map sequence selection. Note the SequenceGroup holds aligned sequences, + * the mappings hold dataset sequences. + */ + AlignedCodonFrame[] codonFrames = sourceAv.getAlignment() + .getCodonFrames(); + SequenceGroup sg = new SequenceGroup(); + for (SequenceI selected : seqsel.getSequences()) + { + for (AlignedCodonFrame acf : codonFrames) + { + SequenceI dnaSeq = acf.getDnaForAaSeq(selected); + if (dnaSeq != null) + { + for (SequenceI seq : av.getAlignment().getSequences()) + { + if (seq.getDatasetSequence() == dnaSeq) + { + sg.addSequence(seq, false); + break; + } + } + } + } + } + av.setSelectionGroup(sg); + av.isSelectionGroupChanged(true); + // ((AlignmentViewport) slave).firePropertyChange("alignment", null, slave + // .getAlignment() + // .getSequences()); + + /* + * Map column selection + */ + // TODO + + AlignmentPanel ap = av.getAlignPanel(); + PaintRefresher.Refresh(this, av.getSequenceSetId()); + + return true; + } } diff --git a/src/jalview/gui/SplitFrame.java b/src/jalview/gui/SplitFrame.java new file mode 100644 index 0000000..a28c0cf --- /dev/null +++ b/src/jalview/gui/SplitFrame.java @@ -0,0 +1,49 @@ +package jalview.gui; + +import jalview.jbgui.GSplitFrame; + +import javax.swing.JComponent; +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() - 10); + + /* + * Add a listener to tidy up when the frame is closed. + */ + 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); + } + }; + }); + + } + +} diff --git a/src/jalview/gui/TreeCanvas.java b/src/jalview/gui/TreeCanvas.java index c5650f5..e700fb3 100755 --- a/src/jalview/gui/TreeCanvas.java +++ b/src/jalview/gui/TreeCanvas.java @@ -20,17 +20,46 @@ */ 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.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 +68,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 +237,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 +287,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 +319,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 +522,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 +712,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 +738,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 +884,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.getSlave() != null) + { + av.getSlave().setSelectionGroup(null); + av.getSlave().getAlignment().deleteAllGroups(); + av.getSlave().clearSequenceColours(); + } colourGroups(); } @@ -923,6 +961,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 +978,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); + if (av.getSlave() != null) + { + SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(sg, av, + av.getSlave()); + if (mappedGroup.getSequences().size() > 0) + { + av.getSlave().getAlignment().addGroup(mappedGroup); + for (SequenceI seq : mappedGroup.getSequences()) + { + av.getSlave().setSequenceColour(seq, + mappedGroup.getIdColour().brighter()); + } + } + } + } + // notify the panel to redo any group specific stuff. for (int a = 0; a < aps.length; a++) { @@ -948,6 +1006,13 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, // to any Jmols listening in } + if (av.getSlave() != null) + { + ((AlignViewport) av.getSlave()).getAlignPanel().updateAnnotation(); + /* + * idPanel. repaint () + */ + } } /** diff --git a/src/jalview/gui/TreePanel.java b/src/jalview/gui/TreePanel.java index ee0bfaf..ce28d39 100755 --- a/src/jalview/gui/TreePanel.java +++ b/src/jalview/gui/TreePanel.java @@ -20,27 +20,43 @@ */ package jalview.gui; -import java.beans.*; -import java.io.*; -import java.util.*; -import java.util.List; - -import javax.imageio.*; - -import java.awt.*; -import java.awt.event.*; -import java.awt.image.*; -import javax.swing.*; - -import org.jibble.epsgraphics.*; -import jalview.analysis.*; +import jalview.analysis.AlignmentSorter; +import jalview.analysis.NJTree; import jalview.commands.CommandI; import jalview.commands.OrderCommand; -import jalview.datamodel.*; -import jalview.io.*; -import jalview.jbgui.*; +import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.AlignmentView; +import jalview.datamodel.BinaryNode; +import jalview.datamodel.ColumnSelection; +import jalview.datamodel.DBRefEntry; +import jalview.datamodel.NodeTransformI; +import jalview.datamodel.SequenceFeature; +import jalview.datamodel.SequenceI; +import jalview.datamodel.SequenceNode; +import jalview.io.JalviewFileChooser; +import jalview.io.JalviewFileView; +import jalview.io.NewickFile; +import jalview.jbgui.GTreePanel; import jalview.util.MessageManager; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.image.BufferedImage; +import java.beans.PropertyChangeEvent; +import java.io.FileOutputStream; +import java.util.ArrayList; +import java.util.List; + +import javax.imageio.ImageIO; +import javax.swing.ButtonGroup; +import javax.swing.JMenuItem; +import javax.swing.JRadioButtonMenuItem; + +import org.jibble.epsgraphics.EpsGraphics2D; + /** * DOCUMENT ME! * @@ -222,7 +238,8 @@ public class TreePanel extends GTreePanel associateLeavesMenu.add(item); } - final JRadioButtonMenuItem itemf = new JRadioButtonMenuItem("All Views"); + final JRadioButtonMenuItem itemf = new JRadioButtonMenuItem( + MessageManager.getString("label.all_views")); buttonGroup.add(itemf); itemf.setSelected(treeCanvas.applyToAllViews); itemf.addActionListener(new ActionListener() @@ -305,7 +322,7 @@ public class TreePanel extends GTreePanel av.setCurrentTree(tree); if (av.getSortByTree()) { - sortByTree_actionPerformed(null); + sortByTree_actionPerformed(); } } } @@ -517,7 +534,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/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/VamsasAppDatastore.java b/src/jalview/io/VamsasAppDatastore.java index 6293a1b..ae3093c 100644 --- a/src/jalview/io/VamsasAppDatastore.java +++ b/src/jalview/io/VamsasAppDatastore.java @@ -127,7 +127,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]); 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/structure/CommandListener.java b/src/jalview/structure/CommandListener.java index 66f39fe..e5f3e36 100644 --- a/src/jalview/structure/CommandListener.java +++ b/src/jalview/structure/CommandListener.java @@ -20,9 +20,11 @@ public interface CommandListener * @param command * @param undo * @param ssm + * @param source + * the originator of the command */ public void mirrorCommand(CommandI command, boolean undo, - StructureSelectionManager ssm); + StructureSelectionManager ssm, VamsasSource source); /** * Temporary workaround to make check for source == listener work. diff --git a/src/jalview/structure/StructureSelectionManager.java b/src/jalview/structure/StructureSelectionManager.java index 5a3beb4..945e08d 100644 --- a/src/jalview/structure/StructureSelectionManager.java +++ b/src/jalview/structure/StructureSelectionManager.java @@ -24,28 +24,25 @@ import jalview.analysis.AlignSeq; import jalview.api.StructureSelectionManagerProvider; import jalview.commands.CommandI; import jalview.commands.EditCommand; -import jalview.commands.EditCommand.Action; -import jalview.commands.EditCommand.Edit; +import jalview.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.Sequence; import jalview.datamodel.SequenceI; import jalview.io.AppletFormatAdapter; +import jalview.util.MappingUtils; import jalview.util.MessageManager; -import jalview.util.StringUtils; import java.io.PrintStream; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.IdentityHashMap; -import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.Vector; @@ -56,7 +53,7 @@ public class StructureSelectionManager { static IdentityHashMap instances; - StructureMapping[] mappings; + private List mappings = new ArrayList(); private boolean processSecondaryStructure = false; @@ -68,6 +65,8 @@ public class StructureSelectionManager private List commandListeners = new ArrayList(); + private List sel_listeners = new ArrayList(); + /** * @return true if will try to use external services for processing secondary * structure @@ -134,17 +133,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); } } } @@ -265,16 +265,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; @@ -501,19 +504,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); } @@ -565,19 +556,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; } } @@ -588,7 +578,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; @@ -600,28 +589,20 @@ 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) - { - for (AlignedCodonFrame acf : seqmappings) - { - acf.markMappedRegion(mappings[j].sequence, indexpos, - results); - } - } + acf.markMappedRegion(sm.sequence, indexpos, results); } } } @@ -655,7 +636,8 @@ 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) { @@ -686,7 +668,13 @@ public class StructureSelectionManager { if (results == null) { - results = buildSearchResults(seq, index); + results = MappingUtils.buildSearchResults(seq, index, + seqmappings); + } + if (handlingVamsasMo) + { + results.addResult(seq, index, index); + } seqListener.highlightSequence(results); } @@ -707,39 +695,6 @@ public class StructureSelectionManager } /** - * Returns a SearchResults object describing the mapped region corresponding - * to the specified sequence position. - * - * @param seq - * @param index - * @return - */ - protected SearchResults buildSearchResults(SequenceI seq, int index) - { - SearchResults results; - results = new SearchResults(); - if (index >= seq.getStart() && index <= seq.getEnd()) - { - if (seqmappings != null) - { - for (AlignedCodonFrame acf : seqmappings) - { - acf.markMappedRegion(seq, index, results); - } - } - // hasSequenceListeners = results.getSize() > 0; - if (handlingVamsasMo) - { - // maybe have to resolve seq to a dataset sequence... - // add in additional direct sequence and/or dataset sequence - // highlighting - results.addResult(seq, index, index); - } - } - return results; - } - - /** * Send suitable messages to a StructureListener to highlight atoms * corresponding to the given sequence position. * @@ -751,26 +706,21 @@ public class StructureSelectionManager int index) { int atomNo; - if (mappings != null) + List atoms = new ArrayList(); + for (StructureMapping sm : mappings) { - List atoms = new ArrayList(); - for (int j = 0; j < mappings.length; j++) + if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()) { - if (mappings[j].sequence == seq - || mappings[j].sequence == seq.getDatasetSequence()) - { - atomNo = mappings[j].getAtomNum(index); + atomNo = sm.getAtomNum(index); - if (atomNo > 0) - { - atoms.add(new AtomSpec(mappings[j].pdbfile, - mappings[j].pdbchain, mappings[j].getPDBResNum(index), - atomNo)); - } + if (atomNo > 0) + { + atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain, sm + .getPDBResNum(index), atomNo)); } } - sl.highlightAtoms(atoms); } + sl.highlightAtoms(atoms); } /** @@ -859,34 +809,25 @@ 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); } } @@ -926,13 +867,11 @@ public class StructureSelectionManager } } - Vector sel_listeners = new Vector(); - public void addSelectionListener(SelectionListener selecter) { if (!sel_listeners.contains(selecter)) { - sel_listeners.addElement(selecter); + sel_listeners.add(selecter); } } @@ -940,7 +879,7 @@ public class StructureSelectionManager { if (sel_listeners.contains(toremove)) { - sel_listeners.removeElement(toremove); + sel_listeners.remove(toremove); } } @@ -948,18 +887,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); } } } @@ -1056,143 +988,36 @@ public class StructureSelectionManager { for (CommandListener listener : commandListeners) { - if (listener.getVamsasSource() != source) - { - listener.mirrorCommand(command, undo, this); - } + listener.mirrorCommand(command, undo, this, source); } } /** - * Returns a new EditCommand representing the given command as mapped to the - * given sequences. If there is no mapping, returns an empty EditCommand. + * 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 targetSeqs + * @param alignmentI * @param gapChar * @return */ - public EditCommand mapEditCommand(EditCommand command, boolean undo, - final List targetSeqs, char gapChar) + public CommandI mapCommand(CommandI command, + boolean undo, + final AlignmentI alignmentI, char gapChar) { - /* - * Cache a copy of the target sequences so we can mimic successive edits on - * them. This lets us compute mappings for all edits in the set. - */ - Map targetCopies = new HashMap(); - for (SequenceI seq : targetSeqs) + if (command instanceof EditCommand) { - SequenceI ds = seq.getDatasetSequence(); - if (ds != null) - { - final Sequence copy = new Sequence("", new String(seq.getSequence())); - copy.setDatasetSequence(ds); - targetCopies.put(ds, copy); - } + return MappingUtils.mapEditCommand((EditCommand) command, undo, + alignmentI, gapChar, + seqmappings); } - - /* - * 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()) + else if (command instanceof OrderCommand) { - Edit edit = edits.next(); - Action action = edit.getAction(); - - /* - * Invert sense of action if an Undo. - */ - if (undo) - { - action = action == Action.INSERT_GAP ? Action.DELETE_GAP - : (action == Action.DELETE_GAP ? Action.INSERT_GAP : action); - } - final int count = edit.getNumber(); - final int editPos = edit.getPosition(); - for (SequenceI seq : edit.getSequences()) - { - /* - * Get residue position at (or to right of) edit location. Note we use - * our 'copy' of the sequence before editing for this. - */ - SequenceI ds = seq.getDatasetSequence(); - if (ds == null) - { - continue; - } - final SequenceI actedOn = originalSequences.get(ds); - final int seqpos = actedOn.findPosition(editPos); - - /* - * Determine all mappings from this position to mapped sequences. - */ - SearchResults sr = buildSearchResults(seq, seqpos); - - if (!sr.isEmpty()) - { - for (SequenceI targetSeq : targetSeqs) - { - ds = targetSeq.getDatasetSequence(); - if (ds == null) - { - continue; - } - SequenceI copyTarget = targetCopies.get(ds); - final int[] match = sr.getResults(copyTarget, 0, - copyTarget.getLength()); - if (match != null) - { - final int ratio = 3; // TODO: compute this - how? - final int mappedCount = count * ratio; - - /* - * Shift Delete start position left, as it acts on positions to - * its right. - */ - int mappedEditPos = action == Action.DELETE_GAP ? match[0] - - mappedCount : match[0]; - Edit e = new EditCommand().new Edit(action, new SequenceI[] - { targetSeq }, mappedEditPos, mappedCount, gapChar); - result.addEdit(e); - - /* - * and 'apply' the edit to our copy of its target sequence - */ - if (action == Action.INSERT_GAP) - { - copyTarget.setSequence(new String(StringUtils.insertCharAt( - copyTarget.getSequence(), mappedEditPos, - mappedCount, - gapChar))); - } - else if (action == Action.DELETE_GAP) - { - copyTarget.setSequence(new String(StringUtils.deleteChars( - copyTarget.getSequence(), mappedEditPos, - mappedEditPos + mappedCount))); - } - } - } - } - /* - * and 'apply' the edit to our copy of its source sequence - */ - if (action == Action.INSERT_GAP) { - actedOn.setSequence(new String(StringUtils.insertCharAt( - actedOn.getSequence(), editPos, count, gapChar))); - } - else if (action == Action.DELETE_GAP) - { - actedOn.setSequence(new String(StringUtils.deleteChars( - actedOn.getSequence(), editPos, editPos + count))); - } - } + return MappingUtils.mapOrderCommand((OrderCommand) command, undo, + alignmentI, seqmappings); } - return result; + return null; } } diff --git a/src/jalview/util/MappingUtils.java b/src/jalview/util/MappingUtils.java new file mode 100644 index 0000000..466aeb8 --- /dev/null +++ b/src/jalview/util/MappingUtils.java @@ -0,0 +1,381 @@ +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.SearchResults; +import jalview.datamodel.Sequence; +import jalview.datamodel.SequenceGroup; +import jalview.datamodel.SequenceI; + +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 alignment + * @param gapChar + * @param mappings + * @return + */ + public static EditCommand mapEditCommand(EditCommand command, + boolean undo, final AlignmentI alignment, char gapChar, + Set mappings) + { + /* + * 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 : alignment.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, alignment.getSequences(), result, + mappings); + } + else if (edit.getAction() == Action.INSERT_GAP + || edit.getAction() == Action.DELETE_GAP) + { + mapInsertOrDelete(edit, undo, originalSequences, + alignment.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); + } + results.addResult(seq, index, index); + } + return results; + } + + /** + * Returns a (possibly empty) SequenceGroup containing any sequences the + * mapped viewport corresponding to the given group in the source viewport. + * + * @param sg + * @param av + * @param mapped + * @return + */ + public static SequenceGroup mapSequenceGroup(SequenceGroup sg, + AlignViewportI av, AlignViewportI mapped) + { + /* + * Map sequence selection. Note the SequenceGroup holds aligned sequences, + * the mappings hold dataset sequences. + */ + AlignedCodonFrame[] codonFrames = av.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 dnaSeq = acf.getDnaForAaSeq(selected); + if (dnaSeq != null) + { + for (SequenceI seq : mapped.getAlignment().getSequences()) + { + if (seq.getDatasetSequence() == dnaSeq) + { + 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) + { + SequenceI dnaSeq = acf.getDnaForAaSeq(seq); + if (dnaSeq != null) + { + for (SequenceI seq2 : mapTo.getSequences()) + { + if (seq2.getDatasetSequence() == dnaSeq) + { + 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; + } + +} diff --git a/src/jalview/viewmodel/AlignmentViewport.java b/src/jalview/viewmodel/AlignmentViewport.java index 1b42faf..ed63e0f 100644 --- a/src/jalview/viewmodel/AlignmentViewport.java +++ b/src/jalview/viewmodel/AlignmentViewport.java @@ -44,6 +44,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; @@ -58,6 +59,11 @@ import java.util.Vector; */ public abstract class AlignmentViewport implements AlignViewportI { + /* + * A viewport that is a slave of (driven by) this one in some sense. + */ + AlignViewportI slave = null; + /** * alignment displayed in the viewport. Please use get/setter */ @@ -803,7 +809,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 @@ -1605,26 +1611,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); @@ -1638,10 +1631,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) @@ -1657,6 +1646,25 @@ public abstract class AlignmentViewport implements AlignViewportI @Override public void clearSequenceColours() { - sequenceColours = null; + sequenceColours.clear(); }; + + @Override + public AlignViewportI getSlave() + { + return this.slave; + } + + @Override + public void setSlave(AlignViewportI sl) + { + if (this == sl.getSlave()) + { + System.err.println("Ignoring recursive setSlave request"); + } + else + { + this.slave = sl; + } + } } diff --git a/test/jalview/analysis/AlignmentUtilsTests.java b/test/jalview/analysis/AlignmentUtilsTests.java index c436818..66f99f3 100644 --- a/test/jalview/analysis/AlignmentUtilsTests.java +++ b/test/jalview/analysis/AlignmentUtilsTests.java @@ -324,62 +324,44 @@ public class AlignmentUtilsTests @Test public void testAlignSequenceAs_withMapping_noIntrons() { - /* - * Simple case: no gaps in dna - */ - SequenceI dna = new Sequence("Seq1", "GGGAAA"); - dna.createDatasetSequence(); - SequenceI protein = new Sequence("Seq1", "-A-L-"); - protein.createDatasetSequence(); - AlignedCodonFrame acf = new AlignedCodonFrame(); MapList map = new MapList(new int[] { 1, 6 }, new int[] { 1, 2 }, 3, 1); - acf.addMap(dna.getDatasetSequence(), protein.getDatasetSequence(), map); /* * No existing gaps in dna: */ - AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-', false, - false); - assertEquals("---GGG---AAA", dna.getSequenceAsString()); + checkAlignSequenceAs("GGGAAA", "-A-L-", false, false, map, + "---GGG---AAA"); /* * Now introduce gaps in dna but ignore them when realigning. */ - dna.setSequence("-G-G-G-A-A-A-"); - AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-', false, - false); - assertEquals("---GGG---AAA", dna.getSequenceAsString()); + 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. */ - dna.setSequence("-G-G--G-A--A-A-"); - AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-', true, - false); - assertEquals("---G-G--G---A--A-A", dna.getSequenceAsString()); + 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. */ - dna.setSequence("-G-GG--AA-A-"); - AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-', true, - true); - assertEquals("---G-GG---AA-A-", dna.getSequenceAsString()); + 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. */ - dna.setSequence("-G-GG--AA-A-"); - AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-', false, - true); - assertEquals("---GGG---AAA-", dna.getSequenceAsString()); + checkAlignSequenceAs("-G-GG--AA-A-", "-A-L-", false, true, map, + "---GGG---AAA-"); } /** @@ -389,62 +371,47 @@ public class AlignmentUtilsTests public void testAlignSequenceAs_withMapping_withIntrons() { /* - * Simple case: no gaps in dna - */ - SequenceI dna = new Sequence("Seq1", "GGGAAACCCTTTGGG"); - dna.createDatasetSequence(); - SequenceI protein = new Sequence("Seq1", "-A-L-"); - protein.createDatasetSequence(); - AlignedCodonFrame acf = new AlignedCodonFrame(); - - /* * Exons at codon 2 (AAA) and 4 (TTT) */ MapList map = new MapList(new int[] { 4, 6, 10, 12 }, new int[] { 1, 2 }, 3, 1); - acf.addMap(dna.getDatasetSequence(), protein.getDatasetSequence(), map); /* - * Align dna as "-A-L-". The protein 'gaps' follow the introns, i.e are - * placed immediately before the mapped codons. + * Simple case: no gaps in dna */ - AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-', false, - false); - assertEquals("GGG---AAACCC---TTTGGG", dna.getSequenceAsString()); + checkAlignSequenceAs("GGGAAACCCTTTGGG", "--A-L-", false, false, map, + "GGG---AAACCCTTTGGG"); /* * Add gaps to dna - but ignore when realigning. */ - dna.setSequence("-G-G-G--A--A---AC-CC-T-TT-GG-G-"); - AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-', false, - false); - assertEquals("GGG---AAACCC---TTTGGG", dna.getSequenceAsString()); + 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. */ - dna.setSequence("-G-G-G--A--A---A-C-CC-T-TT-GG-G-"); - AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-', true, - false); - assertEquals("GGG---A--A---ACCC---T-TTGGG", dna.getSequenceAsString()); + 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. */ - dna.setSequence("-G-G-G--A--A---A-C-CC-T-TT-GG-G-"); - AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-', false, - true); - assertEquals("-G-G-G---AAA-C-CC---TTT-GG-G-", dna.getSequenceAsString()); + 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. */ - dna.setSequence("-G-G-G--A--A---A-C-CC-T-TT-GG-G-"); - AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-', true, - true); - assertEquals("-G-G-G---A--A---A-C-CC---T-TT-GG-G-", - dna.getSequenceAsString()); + 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-"); } /** @@ -453,26 +420,65 @@ public class AlignmentUtilsTests @Test public void testAlignSequenceAs_withMapping_withUnmappedProtein() { - SequenceI dna = new Sequence("Seq1", "GGGAAACCCTTTGGG"); - dna.createDatasetSequence(); - SequenceI protein = new Sequence("Seq1", "-A-L-P-"); - protein.createDatasetSequence(); - AlignedCodonFrame acf = new AlignedCodonFrame(); - + /* * Exons at codon 2 (AAA) and 4 (TTT) mapped to A and P */ - MapList map = new MapList(new int[] + 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() + { + /* - * Align dna as "-A-L-P-". Currently, does nothing (aborts realignment). - * Change this test first if different behaviour wanted. + * Intron GGGAAA followed by exon CCCTTT */ - AlignmentUtils.alignSequenceAs(dna, protein, acf, "---", '-', false, - false); - assertEquals("GGGAAACCCTTTGGG", dna.getSequenceAsString()); + 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/commands/EditCommandTest.java b/test/jalview/commands/EditCommandTest.java index 84671ae..6ea05e6 100644 --- a/test/jalview/commands/EditCommandTest.java +++ b/test/jalview/commands/EditCommandTest.java @@ -405,7 +405,7 @@ public class EditCommandTest * */ @Test - public void testUnwindCommand_multipleInserts() + public void testPriorState_multipleInserts() { EditCommand command = new EditCommand(); SequenceI seq = new Sequence("", "--A--B-CDEF"); @@ -433,7 +433,7 @@ public class EditCommandTest * */ @Test - public void testUnwindCommand_removeAllGaps() + public void testPriorState_removeAllGaps() { EditCommand command = new EditCommand(); SequenceI seq = new Sequence("", "ABC"); @@ -454,7 +454,7 @@ public class EditCommandTest * Test for 'undoing' a single delete edit. */ @Test - public void testUnwindCommand_singleDelete() + public void testPriorState_singleDelete() { EditCommand command = new EditCommand(); SequenceI seq = new Sequence("", "ABCDEF"); @@ -470,11 +470,10 @@ public class EditCommandTest } /** - * Test method that 'restores' edit commands to hold the sequence as it was - * before the edit was applied. + * Test 'undoing' a single gap insertion edit command. */ @Test - public void testUnwindCommand_singleInsert() + public void testPriorState_singleInsert() { EditCommand command = new EditCommand(); SequenceI seq = new Sequence("", "AB---CDEF"); @@ -494,7 +493,7 @@ public class EditCommandTest * for contiguous gaps in each sequence separately. */ @Test - public void testUnwindCommand_removeGapsMultipleSeqs() + public void testPriorState_removeGapsMultipleSeqs() { EditCommand command = new EditCommand(); String original1 = "--ABC-DEF"; @@ -558,7 +557,7 @@ public class EditCommandTest * column region. */ @Test - public void testUnwindCommand_removeGappedCols() + public void testPriorState_removeGappedCols() { EditCommand command = new EditCommand(); String original1 = "--ABC--DEF"; 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); } -- 1.7.10.2