JAL-3081 refactored AnnotationSorter constructor and sort parameters
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Fri, 15 Nov 2019 14:58:53 +0000 (14:58 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Fri, 15 Nov 2019 14:58:53 +0000 (14:58 +0000)
src/jalview/analysis/AnnotationSorter.java
src/jalview/gui/AlignmentPanel.java
test/jalview/analysis/AnnotationSorterTest.java

index f16d9ea..83f3adf 100644 (file)
@@ -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<SequenceI, Integer> 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<? super AlignmentAnnotation> 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<SequenceI, Integer> seqPositions = alignment.getSequencePositions();
 
+    AlignmentAnnotation[] alignmentAnnotations = alignment
+            .getAlignmentAnnotation();
     for (AlignmentAnnotation ann : alignmentAnnotations)
     {
       SequenceI seq = ann.sequenceRef;
index b27d66d..294facc 100644 (file)
@@ -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);
     }
   }
 
index 3277a16..de57b1b 100644 (file)
@@ -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<AlignmentAnnotation> 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);