JAL-3446 AlignmentSorter/Test more documentation; bit cleaner code
authorBobHanson <hansonr@stolaf.edu>
Fri, 5 Jun 2020 14:30:55 +0000 (09:30 -0500)
committerBobHanson <hansonr@stolaf.edu>
Fri, 5 Jun 2020 14:30:55 +0000 (09:30 -0500)
src/jalview/analysis/AlignmentSorter.java

index af9b5df..af7db0a 100755 (executable)
@@ -34,6 +34,7 @@ import jalview.datamodel.SequenceNode;
 import jalview.util.QuickSort;
 
 import java.util.ArrayList;
+import java.util.BitSet;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
@@ -97,7 +98,6 @@ public class AlignmentSorter implements ApplicationSingletonI
 
   boolean sortTreeAscending = true;
 
-
   /**
    * last Annotation Label used for sort by Annotation score
    */
@@ -167,7 +167,7 @@ public class AlignmentSorter implements ApplicationSingletonI
     QuickSort.sort(ids, seqs);
     AlignmentSorter as = getInstance();
     as.sortIdAscending = !as.sortIdAscending;
-    sort(align, seqs, as.sortIdAscending);
+    set(align, seqs, as.sortIdAscending);
   }
 
   /**
@@ -192,7 +192,7 @@ public class AlignmentSorter implements ApplicationSingletonI
     QuickSort.sort(length, seqs);
     AlignmentSorter as = getInstance();
     as.sortLengthAscending = !as.sortLengthAscending;
-    sort(align, seqs, as.sortLengthAscending);
+    set(align, seqs, as.sortLengthAscending);
   }
 
   /**
@@ -257,56 +257,7 @@ public class AlignmentSorter implements ApplicationSingletonI
         tmp.add(orderedseqs[j]);
       }
     }
-    sort(align, tmp, as.sortGroupAscending);
-  }
-
-  /**
-   * Select sequences in order from tmp that is present in mask, and any
-   * remaining sequences in mask not in tmp
-   * 
-   * @param tmp
-   *          thread safe collection of sequences
-   * @param mask
-   *          thread safe collection of sequences
-   * 
-   * @return intersect(tmp,mask)+intersect(complement(tmp),mask)
-   */
-  private static SequenceI[] vectorSubsetToArray(List<SequenceI> tmp,
-          List<SequenceI> mask)
-  {
-    // or?
-    // tmp2 = tmp.retainAll(mask);
-    // return tmp2.addAll(mask.removeAll(tmp2))
-
-    ArrayList<SequenceI> seqs = new ArrayList<>();
-    int i, idx;
-    boolean[] tmask = new boolean[mask.size()];
-
-    for (i = 0; i < mask.size(); i++)
-    {
-      tmask[i] = true;
-    }
-
-    for (i = 0; i < tmp.size(); i++)
-    {
-      SequenceI sq = tmp.get(i);
-      idx = mask.indexOf(sq);
-      if (idx > -1 && tmask[idx])
-      {
-        tmask[idx] = false;
-        seqs.add(sq);
-      }
-    }
-
-    for (i = 0; i < tmask.length; i++)
-    {
-      if (tmask[i])
-      {
-        seqs.add(mask.get(i));
-      }
-    }
-
-    return seqs.toArray(new SequenceI[seqs.size()]);
+    set(align, tmp, as.sortGroupAscending);
   }
 
   /**
@@ -332,7 +283,7 @@ public class AlignmentSorter implements ApplicationSingletonI
     {
       as.sortOrderAscending = true;
     }
-    sort(align, tmp, as.sortOrderAscending);
+    set(align, tmp, as.sortOrderAscending);
   }
 
   /**
@@ -399,7 +350,7 @@ public class AlignmentSorter implements ApplicationSingletonI
     {
       as.sortTreeAscending = !as.sortTreeAscending;
     }
-    sort(align, tmp, as.sortTreeAscending);
+    set(align, tmp, as.sortTreeAscending);
   }
 
   /**
@@ -606,14 +557,15 @@ public class AlignmentSorter implements ApplicationSingletonI
     if (method != FEATURE_SCORE && method != FEATURE_LABEL
             && method != FEATURE_DENSITY)
     {
-      String msg = String
-              .format("Implementation Error - sortByFeature method must be either '%s' or '%s'",
-                      FEATURE_SCORE, FEATURE_DENSITY);
+      String msg = String.format(
+              "Implementation Error - sortByFeature method must be either '%s' or '%s'",
+              FEATURE_SCORE, FEATURE_DENSITY);
       System.err.println(msg);
       return;
     }
 
-    flipFeatureSortIfUnchanged(method, featureTypes, groups, startCol, endCol);
+    flipFeatureSortIfUnchanged(method, featureTypes, groups, startCol,
+            endCol);
 
     SequenceI[] seqs = alignment.getSequencesArray();
 
@@ -632,8 +584,8 @@ public class AlignmentSorter implements ApplicationSingletonI
        * get sequence residues overlapping column region
        * and features for residue positions and specified types
        */
-      String[] types = featureTypes == null ? null : featureTypes
-              .toArray(new String[featureTypes.size()]);
+      String[] types = featureTypes == null ? null
+              : featureTypes.toArray(new String[featureTypes.size()]);
       List<SequenceFeature> sfs = seqs[i].findFeatures(startCol + 1,
               endCol + 1, types);
 
@@ -750,7 +702,8 @@ public class AlignmentSorter implements ApplicationSingletonI
     }
     if (doSort)
     {
-      QuickSort.sortByDouble(scores, seqs, getInstance().sortByFeatureAscending);
+      QuickSort.sortByDouble(scores, seqs,
+              getInstance().sortByFeatureAscending);
     }
     setOrder(alignment, seqs);
   }
@@ -799,13 +752,38 @@ public class AlignmentSorter implements ApplicationSingletonI
     as.sortByFeatureCriteria = scoreCriteria;
   }
 
-  private static void sort(AlignmentI align, List<SequenceI> tmp,
+  /**
+   * Set the alignment's sequences list to contain the sequences from a
+   * temporary list, first adding all the elements from the tmp list, then adding all sequences in the alignment that
+   * are not in the list. Option to do the final sort either in order or in reverse order.
+   * 
+   * @param align The alignment being sorted
+   * @param tmp
+   *          the temporary sequence list
+   * @param ascending
+   *          false for reversed order; only sequences already in
+   *          the alignment will be used (which is actually already guaranteed
+   *          by vectorSubsetToArray)
+   */
+  private static void set(AlignmentI align, List<SequenceI> tmp,
           boolean ascending)
   {
-    sort(align, vectorSubsetToArray(tmp, align.getSequences()), ascending);
+    set(align, vectorSubsetToArray(align.getSequences(), tmp), ascending);
   }
 
-  private static void sort(AlignmentI align, SequenceI[] seqs,
+  /**
+   * Set the alignment's sequences list to contain these sequences, either in
+   * this order or its reverse.
+   * 
+   * @param align
+   * @param seqs
+   *          the new sequence array
+   * @param ascending
+   *          false for reversed order; if ascending, only sequences already in
+   *          the alignment will be used; if descending, then a direct 1:1
+   *          replacement is made
+   */
+  private static void set(AlignmentI align, SequenceI[] seqs,
           boolean ascending)
   {
     if (ascending)
@@ -819,76 +797,119 @@ public class AlignmentSorter implements ApplicationSingletonI
 
   }
 
-
   /**
-   * Sets the Alignment object with the given sequences
+   * Replace the alignment's sequences with values in an array, clearing the
+   * alignment's sequence list and filtering for sequences that are actually in
+   * the alignment already.
    * 
    * @param align
-   *          DOCUMENT ME!
+   *          the Alignment
    * @param seqs
-   *          sequences as an array
+   *          the array of replacement values, of any length
    */
   public static void setOrder(AlignmentI align, SequenceI[] seqs)
   {
     // NOTE: DO NOT USE align.setSequenceAt() here - it will NOT work
-    List<SequenceI> algn = align.getSequences();
-    synchronized (algn)
+    List<SequenceI> seqList = align.getSequences();
+    synchronized (seqList)
     {
       List<SequenceI> tmp = new ArrayList<>();
 
       for (int i = 0; i < seqs.length; i++)
       {
-        if (algn.contains(seqs[i]))
+        if (seqList.contains(seqs[i]))
         {
           tmp.add(seqs[i]);
         }
       }
 
-      algn.clear();
+      seqList.clear();
       // User may have hidden seqs, then clicked undo or redo
       for (int i = 0; i < tmp.size(); i++)
       {
-        algn.add(tmp.get(i));
+        seqList.add(tmp.get(i));
       }
     }
   }
 
   /**
-   * Reverse the order of the sort
+   * Replace the alignment's sequences or a subset of those sequences with
+   * values in an array in reverse order. All sequences are replaced; no check
+   * is made that these sequences are in the alignment already.
    * 
    * @param align
-   *          DOCUMENT ME!
+   *          the Alignment
    * @param seqs
-   *          DOCUMENT ME!
+   *          the array of replacement values, length must be less than or equal
+   *          to Alignment.sequences.size()
    */
   private static void setReverseOrder(AlignmentI align, SequenceI[] seqs)
   {
     int nSeq = seqs.length;
 
     int len = (nSeq + (nSeq % 2)) / 2;
-//    int len = 0;
-//
-//    if ((nSeq % 2) == 0)
-//    {
-//      len = nSeq / 2;
-//    }
-//    else
-//    {
-//      len = (nSeq + 1) / 2;
-//    }
+    // int len = 0;
+    //
+    // if ((nSeq % 2) == 0)
+    // {
+    // len = nSeq / 2;
+    // }
+    // else
+    // {
+    // len = (nSeq + 1) / 2;
+    // }
 
     // NOTE: DO NOT USE align.setSequenceAt() here - it will NOT work
-    List<SequenceI> asq = align.getSequences();
-    synchronized (asq)
+    List<SequenceI> seqList = align.getSequences();
+    synchronized (seqList)
     {
       for (int i = 0; i < len; i++)
       {
         // SequenceI tmp = seqs[i];
-        asq.set(i, seqs[nSeq - i - 1]);
-        asq.set(nSeq - i - 1, seqs[i]);
+        seqList.set(i, seqs[nSeq - i - 1]);
+        seqList.set(nSeq - i - 1, seqs[i]);
       }
     }
   }
 
+  /**
+   * Create and array of reordered sequences in order first from tmp that are
+   * present in seqList already, then, after that, any remaining sequences in
+   * seqList not in tmp. Any sequences in tmp that are not in seqList already
+   * are discarded.
+   * 
+   * @param seqList
+   *          thread safe collection of sequences originally in the alignment
+   * @param tmp
+   *          thread safe collection of sequences or subsequences possibly in
+   *          seqList
+   * 
+   * @return intersect(tmp,seqList)+intersect(complement(tmp),seqList)
+   */
+  private static SequenceI[] vectorSubsetToArray(List<SequenceI> seqList,
+          List<SequenceI> tmp)
+  {
+    ArrayList<SequenceI> seqs = new ArrayList<>();
+    int n = seqList.size();
+    BitSet bs = new BitSet(n);
+    bs.set(0, n);
+    for (int i = 0, nt = tmp.size(); i < nt; i++)
+    {
+      SequenceI sq = tmp.get(i);
+      int idx = seqList.indexOf(sq);
+      if (idx >= 0 && bs.get(idx))
+      {
+        seqs.add(sq);
+        bs.clear(idx);
+      }
+    }
+
+    for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1))
+    {
+      seqs.add(seqList.get(i));
+    }
+
+    return seqs.toArray(new SequenceI[seqs.size()]);
+  }
 
 }