import jalview.analysis.scoremodels.PIDModel;
import jalview.analysis.scoremodels.SimilarityParams;
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
import jalview.datamodel.AlignmentAnnotation;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.AlignmentOrder;
import jalview.util.QuickSort;
import java.util.ArrayList;
+import java.util.BitSet;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
* from the first tobesorted position in the alignment. e.g. (a,tb2,b,tb1,c,tb3
* becomes a,tb1,tb2,tb3,b,c)
*/
-public class AlignmentSorter
+public class AlignmentSorter implements ApplicationSingletonI
{
+
+ private AlignmentSorter()
+ {
+ // private singleton
+ }
+
+ public static AlignmentSorter getInstance()
+ {
+ return (AlignmentSorter) ApplicationSingletonProvider
+ .getInstance(AlignmentSorter.class);
+ }
+
+ /**
+ * types of feature ordering: Sort by score : average score - or total score -
+ * over all features in region Sort by feature label text: (or if null -
+ * feature type text) - numerical or alphabetical Sort by feature density:
+ * based on counts - ignoring individual text or scores for each feature
+ */
+ public static final String FEATURE_SCORE = "average_score";
+
+ public static final String FEATURE_LABEL = "text";
+
+ public static final String FEATURE_DENSITY = "density";
+
/*
* todo: refactor searches to follow a basic pattern: (search property, last
* search state, current sort direction)
*/
- static boolean sortIdAscending = true;
+ boolean sortIdAscending = true;
- static int lastGroupHash = 0;
+ int lastGroupHash = 0;
- static boolean sortGroupAscending = true;
+ boolean sortGroupAscending = true;
- static AlignmentOrder lastOrder = null;
+ AlignmentOrder lastOrder = null;
- static boolean sortOrderAscending = true;
+ boolean sortOrderAscending = true;
- static TreeModel lastTree = null;
+ TreeModel lastTree = null;
- static boolean sortTreeAscending = true;
+ boolean sortTreeAscending = true;
- /*
+ /**
* last Annotation Label used for sort by Annotation score
*/
- private static String lastSortByAnnotation;
+ private String lastSortByAnnotation;
- /*
- * string hash of last arguments to sortByFeature
- * (sort order toggles if this is unchanged between sorts)
+ /**
+ * string hash of last arguments to sortByFeature (sort order toggles if this
+ * is unchanged between sorts)
*/
- private static String sortByFeatureCriteria;
+ private String sortByFeatureCriteria;
- private static boolean sortByFeatureAscending = true;
+ private boolean sortByFeatureAscending = true;
- private static boolean sortLengthAscending;
+ private boolean sortLengthAscending;
private static boolean sortEValueAscending;
}
QuickSort.sort(scores, seqs);
-
setReverseOrder(align, seqs);
}
/**
- * Reverse the order of the sort
- *
- * @param align
- * DOCUMENT ME!
- * @param seqs
- * DOCUMENT ME!
- */
- private static void setReverseOrder(AlignmentI align, SequenceI[] seqs)
- {
- int nSeq = seqs.length;
-
- 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)
- {
- 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]);
- }
- }
- }
-
- /**
- * Sets the Alignment object with the given sequences
- *
- * @param align
- * Alignment object to be updated
- * @param tmp
- * sequences as a vector
- */
- private static void setOrder(AlignmentI align, List<SequenceI> tmp)
- {
- setOrder(align, vectorSubsetToArray(tmp, align.getSequences()));
- }
-
- /**
- * Sets the Alignment object with the given sequences
- *
- * @param align
- * DOCUMENT ME!
- * @param seqs
- * sequences as an array
- */
- 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> tmp = new ArrayList<>();
-
- for (int i = 0; i < seqs.length; i++)
- {
- if (algn.contains(seqs[i]))
- {
- tmp.add(seqs[i]);
- }
- }
-
- algn.clear();
- // User may have hidden seqs, then clicked undo or redo
- for (int i = 0; i < tmp.size(); i++)
- {
- algn.add(tmp.get(i));
- }
- }
- }
-
- /**
* Sorts by ID. Numbers are sorted before letters.
*
* @param align
}
QuickSort.sort(ids, seqs);
-
- if (sortIdAscending)
- {
- setReverseOrder(align, seqs);
- }
- else
- {
- setOrder(align, seqs);
- }
-
- sortIdAscending = !sortIdAscending;
+ AlignmentSorter as = getInstance();
+ as.sortIdAscending = !as.sortIdAscending;
+ set(align, seqs, as.sortIdAscending);
}
/**
}
QuickSort.sort(length, seqs);
-
- if (sortLengthAscending)
- {
- setReverseOrder(align, seqs);
- }
- else
- {
- setOrder(align, seqs);
- }
-
- sortLengthAscending = !sortLengthAscending;
+ AlignmentSorter as = getInstance();
+ as.sortLengthAscending = !as.sortLengthAscending;
+ set(align, seqs, as.sortLengthAscending);
}
/**
// ORDERS BY GROUP SIZE
List<SequenceGroup> groups = new ArrayList<>();
- if (groups.hashCode() != lastGroupHash)
+ AlignmentSorter as = getInstance();
+
+ if (groups.hashCode() != as.lastGroupHash)
{
- sortGroupAscending = true;
- lastGroupHash = groups.hashCode();
+ as.sortGroupAscending = true;
+ as.lastGroupHash = groups.hashCode();
}
else
{
- sortGroupAscending = !sortGroupAscending;
+ as.sortGroupAscending = !as.sortGroupAscending;
}
// SORTS GROUPS BY SIZE
// NOW ADD SEQUENCES MAINTAINING ALIGNMENT ORDER
// /////////////////////////////////////////////
- List<SequenceI> seqs = new ArrayList<>();
+ List<SequenceI> tmp = new ArrayList<>();
for (int i = 0; i < groups.size(); i++)
{
for (int j = 0; j < orderedseqs.length; j++)
{
- seqs.add(orderedseqs[j]);
- }
- }
-
- if (sortGroupAscending)
- {
- setOrder(align, seqs);
- }
- else
- {
- setReverseOrder(align,
- vectorSubsetToArray(seqs, align.getSequences()));
- }
- }
-
- /**
- * 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));
+ tmp.add(orderedseqs[j]);
}
}
-
- return seqs.toArray(new SequenceI[seqs.size()]);
+ set(align, tmp, as.sortGroupAscending);
}
/**
// Get an ordered vector of sequences which may also be present in align
List<SequenceI> tmp = order.getOrder();
- if (lastOrder == order)
- {
- sortOrderAscending = !sortOrderAscending;
- }
- else
- {
- sortOrderAscending = true;
- }
+ AlignmentSorter as = getInstance();
- if (sortOrderAscending)
+ if (as.lastOrder == order)
{
- setOrder(align, tmp);
+ as.sortOrderAscending = !as.sortOrderAscending;
}
else
{
- setReverseOrder(align,
- vectorSubsetToArray(tmp, align.getSequences()));
+ as.sortOrderAscending = true;
}
+ set(align, tmp, as.sortOrderAscending);
}
/**
{
List<SequenceI> tmp = getOrderByTree(align, tree);
- // tmp should properly permute align with tree.
- if (lastTree != tree)
- {
- sortTreeAscending = true;
- lastTree = tree;
- }
- else
- {
- sortTreeAscending = !sortTreeAscending;
- }
+ AlignmentSorter as = getInstance();
- if (sortTreeAscending)
+ // tmp should properly permute align with tree.
+ if (as.lastTree != tree)
{
- setOrder(align, tmp);
+ as.sortTreeAscending = true;
+ as.lastTree = tree;
}
else
{
- setReverseOrder(align,
- vectorSubsetToArray(tmp, align.getSequences()));
+ as.sortTreeAscending = !as.sortTreeAscending;
}
+ set(align, tmp, as.sortTreeAscending);
}
/**
}
jalview.util.QuickSort.sort(scores, seqs);
- if (lastSortByAnnotation != scoreLabel)
+
+ AlignmentSorter as = getInstance();
+
+ if (as.lastSortByAnnotation != scoreLabel)
{
- lastSortByAnnotation = scoreLabel;
+ as.lastSortByAnnotation = scoreLabel;
setOrder(alignment, seqs);
}
else
}
/**
- * types of feature ordering: Sort by score : average score - or total score -
- * over all features in region Sort by feature label text: (or if null -
- * feature type text) - numerical or alphabetical Sort by feature density:
- * based on counts - ignoring individual text or scores for each feature
- */
- public static String FEATURE_SCORE = "average_score";
-
- public static String FEATURE_LABEL = "text";
-
- public static String FEATURE_DENSITY = "density";
-
- /**
* Sort sequences by feature score or density, optionally restricted by
* feature types, feature groups, or alignment start/end positions.
* <p>
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();
* 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);
}
}
+ boolean doSort = false;
+
if (FEATURE_SCORE.equals(method))
{
if (hasScores == 0)
}
}
}
- QuickSort.sortByDouble(scores, seqs, sortByFeatureAscending);
+ doSort = true;
}
else if (FEATURE_DENSITY.equals(method))
{
// System.err.println("Sorting on Density: seq "+seqs[i].getName()+
// " Feats: "+featureCount+" Score : "+scores[i]);
}
- QuickSort.sortByDouble(scores, seqs, sortByFeatureAscending);
+ doSort = true;
+ }
+ if (doSort)
+ {
+ QuickSort.sortByDouble(scores, seqs,
+ getInstance().sortByFeatureAscending);
}
-
setOrder(alignment, seqs);
}
/*
* if resorting on the same criteria, toggle sort order
*/
- if (sortByFeatureCriteria == null
- || !scoreCriteria.equals(sortByFeatureCriteria))
+ AlignmentSorter as = getInstance();
+ if (as.sortByFeatureCriteria == null
+ || !scoreCriteria.equals(as.sortByFeatureCriteria))
{
- sortByFeatureAscending = true;
+ as.sortByFeatureAscending = true;
}
else
{
- sortByFeatureAscending = !sortByFeatureAscending;
+ as.sortByFeatureAscending = !as.sortByFeatureAscending;
}
- sortByFeatureCriteria = scoreCriteria;
+ as.sortByFeatureCriteria = scoreCriteria;
+ }
+
+ /**
+ * 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)
+ {
+ set(align, vectorSubsetToArray(align.getSequences(), tmp), ascending);
+ }
+
+ /**
+ * 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)
+ {
+ setOrder(align, seqs);
+ }
+ else
+ {
+ setReverseOrder(align, seqs);
+ }
+
+ }
+
+ /**
+ * 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
+ * the Alignment
+ * @param seqs
+ * 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> seqList = align.getSequences();
+ synchronized (seqList)
+ {
+ List<SequenceI> tmp = new ArrayList<>();
+
+ for (int i = 0; i < seqs.length; i++)
+ {
+ if (seqList.contains(seqs[i]))
+ {
+ tmp.add(seqs[i]);
+ }
+ }
+
+ seqList.clear();
+ // User may have hidden seqs, then clicked undo or redo
+ for (int i = 0; i < tmp.size(); i++)
+ {
+ seqList.add(tmp.get(i));
+ }
+ }
+ }
+
+ /**
+ * 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
+ * the Alignment
+ * @param seqs
+ * 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;
+ // }
+
+ // NOTE: DO NOT USE align.setSequenceAt() here - it will NOT work
+ List<SequenceI> seqList = align.getSequences();
+ synchronized (seqList)
+ {
+ for (int i = 0; i < len; i++)
+ {
+ // SequenceI tmp = 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()]);
}
}