From a2032664d7eb6954072d43b424e11d219f28df62 Mon Sep 17 00:00:00 2001 From: gmungoc Date: Fri, 15 Nov 2019 14:58:53 +0000 Subject: [PATCH] JAL-3081 refactored AnnotationSorter constructor and sort parameters --- src/jalview/analysis/AnnotationSorter.java | 82 ++++++------ src/jalview/gui/AlignmentPanel.java | 5 +- test/jalview/analysis/AnnotationSorterTest.java | 154 ++++++++++++++--------- 3 files changed, 142 insertions(+), 99 deletions(-) diff --git a/src/jalview/analysis/AnnotationSorter.java b/src/jalview/analysis/AnnotationSorter.java index f16d9ea..83f3adf 100644 --- a/src/jalview/analysis/AnnotationSorter.java +++ b/src/jalview/analysis/AnnotationSorter.java @@ -20,6 +20,7 @@ */ package jalview.analysis; +import jalview.api.AlignViewportI; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.SequenceI; @@ -83,33 +84,35 @@ public class AnnotationSorter } } - // the alignment with respect to which annotations are sorted + /* + * the alignment with respect to which annotations are sorted + */ private final AlignmentI alignment; - // user preference for placement of non-sequence annotations + /* + * if true, autocalculated are sorted first, if false, last + */ private boolean showAutocalcAbove; - // working map of sequence index in alignment + /* + * working map of sequence index in alignment + */ private final Map sequenceIndices = new HashMap<>(); - // if true, sort only repositions auto-calculated annotation (to top or - // bottom) - private final boolean autocalcOnly; + /* + * if true, sort only repositions auto-calculated annotation (to top or bottom) + */ + private boolean autocalcOnly; /** - * Constructor given an alignment and the location (top or bottom) of - * Consensus and similar. + * Constructor * - * @param alignmentI - * @param showAutocalculatedAbove - * @param autoCalcOnly + * @param av */ - public AnnotationSorter(AlignmentI alignmentI, - boolean showAutocalculatedAbove, boolean autoCalcOnly) + public AnnotationSorter(AlignViewportI av) { - this.alignment = alignmentI; - this.showAutocalcAbove = showAutocalculatedAbove; - this.autocalcOnly = autoCalcOnly; + this.alignment = av.getAlignment(); + this.showAutocalcAbove = av.isShowAutocalculatedAbove(); } /** @@ -283,55 +286,56 @@ public class AnnotationSorter }; /** - * Sort by the specified ordering of sequence-specific annotations. + * Sorts by the specified ordering. If order is {@code CUSTOM}, meaning + * annotations have been manually ordered by the user, no sort is performed. * - * @param alignmentAnnotations - * @param order + * @param sortBy + * the sort order to apply + * @param autoCalcOnly + * if true, only autocalculated annotations are repositioned (to top + * or bottom), others are left in their current order */ - public void sort(AlignmentAnnotation[] alignmentAnnotations, - SequenceAnnotationOrder order) + public void sort(SequenceAnnotationOrder sortBy, boolean autoCalcOnly) { - if (alignmentAnnotations == null - || order == SequenceAnnotationOrder.CUSTOM) + if (sortBy == null || sortBy == SequenceAnnotationOrder.CUSTOM) { return; } + this.autocalcOnly = autoCalcOnly; + /* * cache 'alignment sequence positions' if required for sorting */ - if (order == SequenceAnnotationOrder.SEQUENCE_AND_LABEL - || order == SequenceAnnotationOrder.LABEL_AND_SEQUENCE) + if (sortBy == SequenceAnnotationOrder.SEQUENCE_AND_LABEL + || sortBy == SequenceAnnotationOrder.LABEL_AND_SEQUENCE) { - saveSequenceIndices(alignmentAnnotations); + saveSequenceIndices(); } Comparator comparator = getComparator( - order); + sortBy); - if (alignmentAnnotations != null) + AlignmentAnnotation[] annotations = alignment.getAlignmentAnnotation(); + synchronized (annotations) { - synchronized (alignmentAnnotations) - { - Arrays.sort(alignmentAnnotations, comparator); - } + Arrays.sort(annotations, comparator); } } /** - * Calculate and save in a temporary map the position of each annotation's - * sequence (if it has one) in the alignment. Faster to do this once than for - * every annotation comparison. - * - * @param alignmentAnnotations + * Calculates and saves in a temporary map the position of each annotation's + * associated sequence (if it has one) in the alignment. Faster to do this + * once than for every annotation comparison. */ - private void saveSequenceIndices( - AlignmentAnnotation[] alignmentAnnotations) + private void saveSequenceIndices() { sequenceIndices.clear(); Map seqPositions = alignment.getSequencePositions(); + AlignmentAnnotation[] alignmentAnnotations = alignment + .getAlignmentAnnotation(); for (AlignmentAnnotation ann : alignmentAnnotations) { SequenceI seq = ann.sequenceRef; diff --git a/src/jalview/gui/AlignmentPanel.java b/src/jalview/gui/AlignmentPanel.java index b27d66d..294facc 100644 --- a/src/jalview/gui/AlignmentPanel.java +++ b/src/jalview/gui/AlignmentPanel.java @@ -831,9 +831,8 @@ public class AlignmentPanel extends GAlignmentPanel implements SequenceAnnotationOrder sortBy = av.getSortAnnotationsBy(); if (sortBy != SequenceAnnotationOrder.CUSTOM) { - final AnnotationSorter sorter = new AnnotationSorter(getAlignment(), - av.isShowAutocalculatedAbove(), autoCalcOnly); - sorter.sort(getAlignment().getAlignmentAnnotation(), sortBy); + final AnnotationSorter sorter = new AnnotationSorter(av); + sorter.sort(sortBy, autoCalcOnly); } } diff --git a/test/jalview/analysis/AnnotationSorterTest.java b/test/jalview/analysis/AnnotationSorterTest.java index 3277a16..de57b1b 100644 --- a/test/jalview/analysis/AnnotationSorterTest.java +++ b/test/jalview/analysis/AnnotationSorterTest.java @@ -23,14 +23,16 @@ package jalview.analysis; import static org.testng.AssertJUnit.assertEquals; import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder; +import jalview.api.AlignViewportI; +import jalview.bin.Cache; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentAnnotation; +import jalview.datamodel.AlignmentI; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceI; +import jalview.gui.AlignViewport; import jalview.gui.JvOptionPane; -import java.util.ArrayList; -import java.util.List; import java.util.Random; import org.testng.annotations.BeforeClass; @@ -53,9 +55,21 @@ public class AnnotationSorterTest private static final String SS = "secondary structure"; - AlignmentAnnotation[] anns = new AlignmentAnnotation[0]; + AlignViewportI av = null; - Alignment al = null; + /** + * Configure so that the viewport does not create autocalculated annotation - + * test methods flag selected annotation as autocalculated instead + */ + @BeforeClass(alwaysRun = true) + public void setUpBeforeClass() + { + Cache.loadProperties("test/jalview/io/testProps.jvprops"); + Cache.setProperty("SHOW_QUALITY", "false"); + Cache.setProperty("SHOW_CONSERVATION", "false"); + Cache.setProperty("SHOW_IDENTITY", "false"); + Cache.setProperty("SHOW_OCCUPANCY", "false"); + } /* * Set up 6 sequences and 7 annotations. @@ -63,43 +77,34 @@ public class AnnotationSorterTest @BeforeMethod(alwaysRun = true) public void setUp() { - al = buildAlignment(NUM_SEQS); - anns = buildAnnotations(NUM_ANNS); - } - - /** - * Construct an array of numAnns annotations - * - * @param numAnns - * - * @return - */ - protected AlignmentAnnotation[] buildAnnotations(int numAnns) - { - List annlist = new ArrayList<>(); - for (int i = 0; i < numAnns; i++) - { - AlignmentAnnotation ann = new AlignmentAnnotation(SS + i, "", 0); - annlist.add(ann); - } - return annlist.toArray(anns); + av = buildAlignment(NUM_SEQS, NUM_ANNS); } /** - * Make an alignment with numSeqs sequences in it. + * Make an alignment viewport with numSeqs sequences and numAnns annotations + * in it * * @param numSeqs + * @param numAnns * * @return */ - private Alignment buildAlignment(int numSeqs) + private AlignViewportI buildAlignment(int numSeqs, int numAnns) { SequenceI[] seqs = new Sequence[numSeqs]; for (int i = 0; i < numSeqs; i++) { seqs[i] = new Sequence("Sequence" + i, "axrdkfp"); } - return new Alignment(seqs); + Alignment al = new Alignment(seqs); + + for (int i = 0; i < numAnns; i++) + { + AlignmentAnnotation ann = new AlignmentAnnotation(SS + i, "", 0); + al.addAnnotation(ann); + } + + return new AlignViewport(al); } /** @@ -117,6 +122,9 @@ public class AnnotationSorterTest @Test(groups = { "Functional" }) public void testSortBySequenceAndLabel_autocalcLast() { + AlignmentI al = av.getAlignment(); + AlignmentAnnotation[] anns = al.getAlignmentAnnotation(); + // @formatter:off anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0"; anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure"; @@ -127,8 +135,9 @@ public class AnnotationSorterTest anns[6].sequenceRef = al.getSequenceAt(3); anns[6].label = "IRP"; // @formatter:on - AnnotationSorter testee = new AnnotationSorter(al, false, false); - testee.sort(anns, SequenceAnnotationOrder.SEQUENCE_AND_LABEL); + av.setShowAutocalculatedAbove(false); + AnnotationSorter testee = new AnnotationSorter(av); + testee.sort(SequenceAnnotationOrder.SEQUENCE_AND_LABEL, false); assertEquals("label5", anns[0].label); // for sequence 0 assertEquals("label0", anns[1].label); // for sequence 1 assertEquals("iron", anns[2].label); // sequence 3 /iron @@ -144,6 +153,9 @@ public class AnnotationSorterTest @Test(groups = { "Functional" }) public void testSortBySequenceAndLabel_autocalcFirst() { + AlignmentI al = av.getAlignment(); + AlignmentAnnotation[] anns = al.getAlignmentAnnotation(); + // @formatter:off anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0"; anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure"; @@ -154,8 +166,9 @@ public class AnnotationSorterTest anns[6].sequenceRef = al.getSequenceAt(3); anns[6].label = "IRP"; // @formatter:on - AnnotationSorter testee = new AnnotationSorter(al, true, false); - testee.sort(anns, SequenceAnnotationOrder.SEQUENCE_AND_LABEL); + av.setShowAutocalculatedAbove(true); + AnnotationSorter testee = new AnnotationSorter(av); + testee.sort(SequenceAnnotationOrder.SEQUENCE_AND_LABEL, false); assertEquals("Quality", anns[0].label); // autocalc annotations assertEquals("Consensus", anns[1].label); // retain ordering assertEquals("label5", anns[2].label); // for sequence 0 @@ -180,6 +193,9 @@ public class AnnotationSorterTest @Test(groups = { "Functional" }) public void testSortByLabelAndSequence_autocalcLast() { + AlignmentI al = av.getAlignment(); + AlignmentAnnotation[] anns = al.getAlignmentAnnotation(); + // @formatter:off anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0"; anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure"; @@ -190,8 +206,9 @@ public class AnnotationSorterTest anns[6].sequenceRef = al.getSequenceAt(2); anns[6].label = "Structure"; // @formatter:on - AnnotationSorter testee = new AnnotationSorter(al, false, false); - testee.sort(anns, SequenceAnnotationOrder.LABEL_AND_SEQUENCE); + av.setShowAutocalculatedAbove(false); + AnnotationSorter testee = new AnnotationSorter(av); + testee.sort(SequenceAnnotationOrder.LABEL_AND_SEQUENCE, false); assertEquals("IRON", anns[0].label); // IRON / sequence 0 assertEquals("iron", anns[1].label); // iron / sequence 3 assertEquals("label0", anns[2].label); // label0 / sequence 1 @@ -207,6 +224,9 @@ public class AnnotationSorterTest @Test(groups = { "Functional" }) public void testSortByLabelAndSequence_autocalcFirst() { + AlignmentI al = av.getAlignment(); + AlignmentAnnotation[] anns = al.getAlignmentAnnotation(); + // @formatter:off anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0"; anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure"; @@ -217,8 +237,9 @@ public class AnnotationSorterTest anns[6].sequenceRef = al.getSequenceAt(2); anns[6].label = "Structure"; // @formatter:on - AnnotationSorter testee = new AnnotationSorter(al, true, false); - testee.sort(anns, SequenceAnnotationOrder.LABEL_AND_SEQUENCE); + av.setShowAutocalculatedAbove(true); + AnnotationSorter testee = new AnnotationSorter(av); + testee.sort(SequenceAnnotationOrder.LABEL_AND_SEQUENCE, false); assertEquals("Quality", anns[0].label); // autocalc annotations assertEquals("Consensus", anns[1].label); // retain ordering assertEquals("IRON", anns[2].label); // IRON / sequence 0 @@ -235,6 +256,9 @@ public class AnnotationSorterTest @Test(groups = { "Functional" }) public void testNoSort_autocalcFirst() { + AlignmentI al = av.getAlignment(); + AlignmentAnnotation[] anns = al.getAlignmentAnnotation(); + // @formatter:off anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0"; anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure"; @@ -245,8 +269,9 @@ public class AnnotationSorterTest anns[6].sequenceRef = al.getSequenceAt(2); anns[6].label = "Structure"; // @formatter:on - AnnotationSorter testee = new AnnotationSorter(al, true, false); - testee.sort(anns, SequenceAnnotationOrder.NONE); + av.setShowAutocalculatedAbove(true); + AnnotationSorter testee = new AnnotationSorter(av); + testee.sort(SequenceAnnotationOrder.NONE, false); assertEquals("Quality", anns[0].label); // autocalc annotations assertEquals("Consensus", anns[1].label); // retain ordering assertEquals("label0", anns[2].label); @@ -272,8 +297,9 @@ public class AnnotationSorterTest */ private void testTiming_presorted(final int numSeqs, final int numAnns) { - Alignment alignment = buildAlignment(numSeqs); - AlignmentAnnotation[] annotations = buildAnnotations(numAnns); + AlignViewportI viewport = buildAlignment(numSeqs, numAnns); + AlignmentI alignment = viewport.getAlignment(); + AlignmentAnnotation[] annotations = alignment.getAlignmentAnnotation(); /* * Set the annotations presorted by label @@ -287,8 +313,9 @@ public class AnnotationSorterTest annotations[i].label = "label" + i; } long startTime = System.currentTimeMillis(); - AnnotationSorter testee = new AnnotationSorter(alignment, false, false); - testee.sort(annotations, SequenceAnnotationOrder.LABEL_AND_SEQUENCE); + viewport.setShowAutocalculatedAbove(false); + AnnotationSorter testee = new AnnotationSorter(viewport); + testee.sort(SequenceAnnotationOrder.LABEL_AND_SEQUENCE, false); long endTime = System.currentTimeMillis(); final long elapsed = endTime - startTime; System.out.println("Timing test for presorted " + numSeqs @@ -316,8 +343,9 @@ public class AnnotationSorterTest */ private void testTiming_unsorted(final int numSeqs, final int numAnns) { - Alignment alignment = buildAlignment(numSeqs); - AlignmentAnnotation[] annotations = buildAnnotations(numAnns); + AlignViewportI viewport = buildAlignment(numSeqs, numAnns); + AlignmentI alignment = viewport.getAlignment(); + AlignmentAnnotation[] annotations = alignment.getAlignmentAnnotation(); /* * Set the annotations in random order with respect to the sequences @@ -331,8 +359,9 @@ public class AnnotationSorterTest annotations[i].label = "label" + i; } long startTime = System.currentTimeMillis(); - AnnotationSorter testee = new AnnotationSorter(alignment, false, false); - testee.sort(annotations, SequenceAnnotationOrder.SEQUENCE_AND_LABEL); + av.setShowAutocalculatedAbove(false); + AnnotationSorter testee = new AnnotationSorter(av); + testee.sort(SequenceAnnotationOrder.SEQUENCE_AND_LABEL, false); long endTime = System.currentTimeMillis(); final long elapsed = endTime - startTime; System.out.println("Timing test for unsorted " + numSeqs @@ -366,8 +395,9 @@ public class AnnotationSorterTest */ private void testTiming_semiSorted(final int numSeqs, final int numAnns) { - Alignment alignment = buildAlignment(numSeqs); - AlignmentAnnotation[] annotations = buildAnnotations(numAnns); + AlignViewportI viewport = buildAlignment(numSeqs, numAnns); + AlignmentI alignment = viewport.getAlignment(); + AlignmentAnnotation[] annotations = alignment.getAlignmentAnnotation(); String[] labels = new String[] { "label1", "label2", "label3", "label4", "label5", "label6" }; @@ -384,8 +414,9 @@ public class AnnotationSorterTest annotations[i].label = labels[r.nextInt(labels.length)]; } long startTime = System.currentTimeMillis(); - AnnotationSorter testee = new AnnotationSorter(alignment, false, false); - testee.sort(annotations, SequenceAnnotationOrder.LABEL_AND_SEQUENCE); + av.setShowAutocalculatedAbove(false); + AnnotationSorter testee = new AnnotationSorter(av); + testee.sort(SequenceAnnotationOrder.LABEL_AND_SEQUENCE, false); long endTime = System.currentTimeMillis(); long elapsed = endTime - startTime; System.out.println("Sort by label for semisorted " + numSeqs @@ -394,7 +425,7 @@ public class AnnotationSorterTest // now resort by sequence startTime = System.currentTimeMillis(); - testee.sort(annotations, SequenceAnnotationOrder.SEQUENCE_AND_LABEL); + testee.sort(SequenceAnnotationOrder.SEQUENCE_AND_LABEL, false); endTime = System.currentTimeMillis(); elapsed = endTime - startTime; System.out.println("Resort by sequence for semisorted " + numSeqs @@ -403,7 +434,7 @@ public class AnnotationSorterTest // now resort by label startTime = System.currentTimeMillis(); - testee.sort(annotations, SequenceAnnotationOrder.LABEL_AND_SEQUENCE); + testee.sort(SequenceAnnotationOrder.LABEL_AND_SEQUENCE, false); endTime = System.currentTimeMillis(); elapsed = endTime - startTime; System.out.println("Resort by label for semisorted " + numSeqs @@ -418,6 +449,9 @@ public class AnnotationSorterTest @Test(groups = { "Functional" }) public void testSort_custom() { + AlignmentI al = av.getAlignment(); + AlignmentAnnotation[] anns = al.getAlignmentAnnotation(); + // @formatter:off anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0"; anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure"; @@ -431,8 +465,9 @@ public class AnnotationSorterTest /* * showAutocalcAbove=true ignored if CUSTOM ordering */ - AnnotationSorter testee = new AnnotationSorter(al, true, false); - testee.sort(anns, SequenceAnnotationOrder.CUSTOM); + av.setShowAutocalculatedAbove(true); + AnnotationSorter testee = new AnnotationSorter(av); + testee.sort(SequenceAnnotationOrder.CUSTOM, false); assertEquals("label0", anns[0].label); // all unchanged assertEquals("structure", anns[1].label); assertEquals("iron", anns[2].label); @@ -448,6 +483,9 @@ public class AnnotationSorterTest @Test(groups = { "Functional" }) public void testSort_autocalcOnly() { + AlignmentI al = av.getAlignment(); + AlignmentAnnotation[] anns = al.getAlignmentAnnotation(); + // @formatter:off anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0"; anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure"; @@ -461,8 +499,9 @@ public class AnnotationSorterTest /* * showAutocalcAbove=true, autocalcOnly=true */ - AnnotationSorter testee = new AnnotationSorter(al, true, true); - testee.sort(anns, SequenceAnnotationOrder.LABEL_AND_SEQUENCE); + av.setShowAutocalculatedAbove(true); + AnnotationSorter testee = new AnnotationSorter(av); + testee.sort(SequenceAnnotationOrder.LABEL_AND_SEQUENCE, true); assertEquals("Quality", anns[0].label); // moved to top assertEquals("Consensus", anns[1].label); // moved to top assertEquals("label0", anns[2].label); // the rest unchanged @@ -474,8 +513,9 @@ public class AnnotationSorterTest /* * showAutocalcAbove=false, autocalcOnly=true */ - testee = new AnnotationSorter(al, false, true); - testee.sort(anns, SequenceAnnotationOrder.LABEL_AND_SEQUENCE); + av.setShowAutocalculatedAbove(false); + testee = new AnnotationSorter(av); + testee.sort(SequenceAnnotationOrder.LABEL_AND_SEQUENCE, true); assertEquals("label0", anns[0].label); // unchanged assertEquals("structure", anns[1].label); assertEquals("iron", anns[2].label); -- 1.7.10.2