Clover icon

Coverage Report

  1. Project Clover database Thu Dec 4 2025 16:11:35 GMT
  2. Package jalview.analysis

File AlignmentSorter.java

 

Coverage histogram

../../img/srcFileCovDistChart6.png
37% of files have more coverage

Code metrics

144
274
23
1
1,024
598
106
0.39
11.91
23
4.61

Classes

Class Line # Actions
AlignmentSorter 58 274 106
0.5192743551.9%
 

Contributing tests

This file is covered by 9 tests. .

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ The Jalview Authors
4    *
5    * This file is part of Jalview.
6    *
7    * Jalview is free software: you can redistribute it and/or
8    * modify it under the terms of the GNU General Public License
9    * as published by the Free Software Foundation, either version 3
10    * of the License, or (at your option) any later version.
11    *
12    * Jalview is distributed in the hope that it will be useful, but
13    * WITHOUT ANY WARRANTY; without even the implied warranty
14    * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15    * PURPOSE. See the GNU General Public License for more details.
16    *
17    * You should have received a copy of the GNU General Public License
18    * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19    * The Jalview Authors are detailed in the 'AUTHORS' file.
20    */
21    package jalview.analysis;
22   
23    import jalview.analysis.scoremodels.PIDModel;
24    import jalview.analysis.scoremodels.SimilarityParams;
25    import jalview.bin.ApplicationSingletonProvider;
26    import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
27    import jalview.datamodel.AlignmentAnnotation;
28    import jalview.datamodel.AlignmentI;
29    import jalview.datamodel.AlignmentOrder;
30    import jalview.datamodel.BinaryNode;
31    import jalview.datamodel.SequenceFeature;
32    import jalview.datamodel.SequenceGroup;
33    import jalview.datamodel.SequenceI;
34    import jalview.datamodel.SequenceNode;
35    import jalview.util.QuickSort;
36   
37    import java.util.ArrayList;
38    import java.util.BitSet;
39    import java.util.Collections;
40    import java.util.Iterator;
41    import java.util.List;
42   
43    /**
44    * Routines for manipulating the order of a multiple sequence alignment TODO:
45    * this class retains some global states concerning sort-order which should be
46    * made attributes for the caller's alignment visualization. TODO: refactor to
47    * allow a subset of selected sequences to be sorted within the context of a
48    * whole alignment. Sort method template is: SequenceI[] tobesorted, [ input
49    * data mapping to each tobesorted element to use ], Alignment context of
50    * tobesorted that are to be re-ordered, boolean sortinplace, [special data - ie
51    * seuqence to be sorted w.r.t.]) sortinplace implies that the sorted vector
52    * resulting from applying the operation to tobesorted should be mapped back to
53    * the original positions in alignment. Otherwise, normal behaviour is to re
54    * order alignment so that tobesorted is sorted and grouped together starting
55    * from the first tobesorted position in the alignment. e.g. (a,tb2,b,tb1,c,tb3
56    * becomes a,tb1,tb2,tb3,b,c)
57    */
 
58    public class AlignmentSorter implements ApplicationSingletonI
59    {
60   
 
61  2 toggle private AlignmentSorter()
62    {
63    // private singleton
64    }
65   
 
66  18 toggle public static AlignmentSorter getInstance()
67    {
68  18 return ApplicationSingletonProvider.getInstance(AlignmentSorter.class);
69    }
70   
71    /**
72    * types of feature ordering: Sort by score : average score - or total score -
73    * over all features in region Sort by feature label text: (or if null -
74    * feature type text) - numerical or alphabetical Sort by feature density:
75    * based on counts - ignoring individual text or scores for each feature
76    */
77    public static final String FEATURE_SCORE = "average_score";
78   
79    public static final String FEATURE_LABEL = "text";
80   
81    public static final String FEATURE_DENSITY = "density";
82   
83    /*
84    * todo: refactor searches to follow a basic pattern: (search property, last
85    * search state, current sort direction)
86    */
87    boolean sortIdAscending = true;
88   
89    boolean sortDescripionAscending = true;
90   
91    int lastGroupHash = 0;
92   
93    boolean sortGroupAscending = true;
94   
95    AlignmentOrder lastOrder = null;
96   
97    boolean sortOrderAscending = true;
98   
99    TreeModel lastTree = null;
100   
101    boolean sortTreeAscending = true;
102   
103    /**
104    * last Annotation Label used for sort by Annotation score
105    */
106    private String lastSortByAnnotation;
107   
108    /**
109    * string hash of last arguments to sortByFeature (sort order toggles if this
110    * is unchanged between sorts)
111    */
112    private String sortByFeatureCriteria;
113   
114    private boolean sortByFeatureAscending = true;
115   
116    private boolean sortLengthAscending;
117   
118    private boolean sortEValueAscending;
119   
120    private boolean sortBitScoreAscending;
121   
122    /**
123    * Sorts sequences in the alignment by Percentage Identity with the given
124    * reference sequence, sorting the highest identity to the top
125    *
126    * @param align
127    * AlignmentI
128    * @param s
129    * SequenceI
130    * @param end
131    */
 
132  0 toggle public static void sortByPID(AlignmentI align, SequenceI s)
133    {
134  0 int nSeq = align.getHeight();
135   
136  0 float[] scores = new float[nSeq];
137  0 SequenceI[] seqs = new SequenceI[nSeq];
138  0 String refSeq = s.getSequenceAsString();
139   
140  0 SimilarityParams pidParams = new SimilarityParams(true, true, true,
141    true);
142  0 for (int i = 0; i < nSeq; i++)
143    {
144  0 scores[i] = (float) PIDModel.computePID(
145    align.getSequenceAt(i).getSequenceAsString(), refSeq,
146    pidParams);
147  0 seqs[i] = align.getSequenceAt(i);
148    }
149   
150  0 QuickSort.sort(scores, seqs);
151  0 setReverseOrder(align, seqs);
152    }
153   
154    /**
155    * Sorts by ID. Numbers are sorted before letters.
156    *
157    * @param align
158    * The alignment object to sort
159    */
 
160  0 toggle public static void sortByID(AlignmentI align)
161    {
162  0 int nSeq = align.getHeight();
163   
164  0 String[] ids = new String[nSeq];
165  0 SequenceI[] seqs = new SequenceI[nSeq];
166   
167  0 for (int i = 0; i < nSeq; i++)
168    {
169  0 ids[i] = align.getSequenceAt(i).getName();
170  0 seqs[i] = align.getSequenceAt(i);
171    }
172   
173  0 QuickSort.sort(ids, seqs);
174  0 AlignmentSorter as = getInstance();
175  0 as.sortIdAscending = !as.sortIdAscending;
176  0 set(align, seqs, as.sortIdAscending);
177    }
178   
179    /**
180    * Sorts by Description. Numbers are sorted before letters.
181    *
182    * @param align
183    * The alignment object to sort
184    */
 
185  0 toggle public static void sortByDescription(AlignmentI align)
186    {
187  0 int nSeq = align.getHeight();
188   
189  0 String[] ids = new String[nSeq];
190  0 SequenceI[] seqs = new SequenceI[nSeq];
191   
192  0 for (int i = 0; i < nSeq; i++)
193    {
194  0 ids[i] = align.getSequenceAt(i).getDescription() != null
195    ? align.getSequenceAt(i).getDescription()
196    : "";
197  0 seqs[i] = align.getSequenceAt(i);
198    }
199   
200  0 QuickSort.sort(ids, seqs);
201   
202  0 AlignmentSorter as = getInstance();
203  0 as.sortDescripionAscending = !as.sortDescripionAscending;
204  0 set(align,seqs,as.sortDescripionAscending);
205    }
206   
207    /**
208    * Sorts by sequence length
209    *
210    * @param align
211    * The alignment object to sort
212    */
 
213  0 toggle public static void sortByLength(AlignmentI align)
214    {
215  0 int nSeq = align.getHeight();
216   
217  0 float[] length = new float[nSeq];
218  0 SequenceI[] seqs = new SequenceI[nSeq];
219   
220  0 for (int i = 0; i < nSeq; i++)
221    {
222  0 seqs[i] = align.getSequenceAt(i);
223  0 length[i] = (seqs[i].getEnd() - seqs[i].getStart());
224    }
225   
226  0 QuickSort.sort(length, seqs);
227  0 AlignmentSorter as = getInstance();
228  0 as.sortLengthAscending = !as.sortLengthAscending;
229  0 set(align, seqs, as.sortLengthAscending);
230    }
231   
232    /**
233    * Sorts by sequence evalue. Currently moves all sequences without an evalue to
234    * the top of the alignment.
235    *
236    * @param align
237    * The alignment object to sort
238    */
 
239  0 toggle public static void sortByEValue(AlignmentI align)
240    {
241  0 int nSeq = align.getHeight();
242   
243  0 double[] evalue = new double[nSeq];
244  0 SequenceI[] seqs = new SequenceI[nSeq];
245   
246  0 for (int i = 0; i < nSeq; i++)
247    {
248  0 seqs[i] = align.getSequenceAt(i);
249  0 AlignmentAnnotation[] ann = seqs[i].getAnnotation("Search Scores");
250  0 if (ann != null)
251    {
252  0 evalue[i] = ann[0].getEValue();
253    }
254    else
255    {
256  0 evalue[i] = -1;
257    }
258    }
259   
260  0 QuickSort.sort(evalue, seqs);
261   
262  0 AlignmentSorter as = getInstance();
263  0 as.sortEValueAscending = !as.sortEValueAscending;
264  0 set(align, seqs, as.sortEValueAscending);
265    }
266   
267    /**
268    * Sorts by sequence bit score. Currently moves all sequences without a bit
269    * score to the top of the alignment
270    *
271    * @param align
272    * The alignment object to sort
273    */
 
274  0 toggle public static void sortByBitScore(AlignmentI align)
275    {
276  0 int nSeq = align.getHeight();
277   
278  0 double[] score = new double[nSeq];
279  0 SequenceI[] seqs = new SequenceI[nSeq];
280   
281  0 for (int i = 0; i < nSeq; i++)
282    {
283  0 seqs[i] = align.getSequenceAt(i);
284  0 AlignmentAnnotation[] ann = seqs[i].getAnnotation("Search Scores");
285  0 if (ann != null)
286    {
287  0 score[i] = ann[0].getEValue();
288    }
289    else
290    {
291  0 score[i] = -1;
292    }
293    }
294   
295  0 QuickSort.sort(score, seqs);
296   
297   
298  0 AlignmentSorter as = getInstance();
299   
300  0 as.sortBitScoreAscending = !as.sortBitScoreAscending;
301  0 set(align, seqs, as.sortBitScoreAscending);
302    }
303   
304    /**
305    * Sorts the alignment by size of group. <br>
306    * Maintains the order of sequences in each group by order in given alignment
307    * object.
308    *
309    * @param align
310    * sorts the given alignment object by group
311    */
 
312  4 toggle public static void sortByGroup(AlignmentI align)
313    {
314    // MAINTAINS ORIGNAL SEQUENCE ORDER,
315    // ORDERS BY GROUP SIZE
316  4 List<SequenceGroup> groups = new ArrayList<>();
317   
318  4 AlignmentSorter as = getInstance();
319   
320  4 if (groups.hashCode() != as.lastGroupHash)
321    {
322  1 as.sortGroupAscending = true;
323  1 as.lastGroupHash = groups.hashCode();
324    }
325    else
326    {
327  3 as.sortGroupAscending = !as.sortGroupAscending;
328    }
329   
330    // SORTS GROUPS BY SIZE
331    // ////////////////////
332  4 for (SequenceGroup sg : align.getGroups())
333    {
334  44 for (int j = 0; j < groups.size(); j++)
335    {
336  32 SequenceGroup sg2 = groups.get(j);
337   
338  32 if (sg.getSize() > sg2.getSize())
339    {
340  8 groups.add(j, sg);
341   
342  8 break;
343    }
344    }
345   
346  20 if (!groups.contains(sg))
347    {
348  12 groups.add(sg);
349    }
350    }
351   
352    // NOW ADD SEQUENCES MAINTAINING ALIGNMENT ORDER
353    // /////////////////////////////////////////////
354  4 List<SequenceI> tmp = new ArrayList<>();
355   
356  24 for (int i = 0; i < groups.size(); i++)
357    {
358  20 SequenceGroup sg = groups.get(i);
359  20 SequenceI[] orderedseqs = sg.getSequencesInOrder(align);
360   
361  152 for (int j = 0; j < orderedseqs.length; j++)
362    {
363  132 tmp.add(orderedseqs[j]);
364    }
365    }
366  4 set(align, tmp, as.sortGroupAscending);
367    }
368   
369    /**
370    * Sorts by a given AlignmentOrder object
371    *
372    * @param align
373    * Alignment to order
374    * @param order
375    * specified order for alignment
376    */
 
377  0 toggle public static void sortBy(AlignmentI align, AlignmentOrder order)
378    {
379    // Get an ordered vector of sequences which may also be present in align
380  0 List<SequenceI> tmp = order.getOrder();
381   
382  0 AlignmentSorter as = getInstance();
383   
384  0 if (as.lastOrder == order)
385    {
386  0 as.sortOrderAscending = !as.sortOrderAscending;
387    }
388    else
389    {
390  0 as.sortOrderAscending = true;
391    }
392  0 set(align, tmp, as.sortOrderAscending);
393    }
394   
395    /**
396    * DOCUMENT ME!
397    *
398    * @param align
399    * alignment to order
400    * @param tree
401    * tree which has
402    *
403    * @return DOCUMENT ME!
404    */
 
405  1 toggle private static List<SequenceI> getOrderByTree(AlignmentI align,
406    TreeModel tree)
407    {
408  1 int nSeq = align.getHeight();
409   
410  1 List<SequenceI> tmp = new ArrayList<>();
411   
412  1 tmp = _sortByTree(tree.getTopNode(), tmp, align.getSequences());
413   
414  1 if (tmp.size() != nSeq)
415    {
416    // TODO: JBPNote - decide if this is always an error
417    // (eg. not when a tree is associated to another alignment which has more
418    // sequences)
419  0 if (tmp.size() != nSeq)
420    {
421  0 addStrays(align, tmp);
422    }
423   
424  0 if (tmp.size() != nSeq)
425    {
426  0 jalview.bin.Console.errPrintln("WARNING: tmp.size()=" + tmp.size()
427    + " != nseq=" + nSeq
428    + " in getOrderByTree - tree contains sequences not in alignment");
429    }
430    }
431   
432  1 return tmp;
433    }
434   
435    /**
436    * Sorts the alignment by a given tree
437    *
438    * @param align
439    * alignment to order
440    * @param tree
441    * tree which has
442    */
 
443  1 toggle public static void sortByTree(AlignmentI align, TreeModel tree)
444    {
445  1 List<SequenceI> tmp = getOrderByTree(align, tree);
446   
447  1 AlignmentSorter as = getInstance();
448   
449    // tmp should properly permute align with tree.
450  1 if (as.lastTree != tree)
451    {
452  1 as.sortTreeAscending = true;
453  1 as.lastTree = tree;
454    }
455    else
456    {
457  0 as.sortTreeAscending = !as.sortTreeAscending;
458    }
459  1 set(align, tmp, as.sortTreeAscending);
460    }
461   
462    /**
463    * DOCUMENT ME!
464    *
465    * @param align
466    * DOCUMENT ME!
467    * @param tmp
468    * DOCUMENT ME!
469    */
 
470  0 toggle private static void addStrays(AlignmentI align, List<SequenceI> tmp)
471    {
472  0 int nSeq = align.getHeight();
473   
474  0 for (int i = 0; i < nSeq; i++)
475    {
476  0 if (!tmp.contains(align.getSequenceAt(i)))
477    {
478  0 tmp.add(align.getSequenceAt(i));
479    }
480    }
481   
482  0 if (nSeq != tmp.size())
483    {
484  0 System.err
485    .println("ERROR: Size still not right even after addStrays");
486    }
487    }
488   
489    /**
490    * DOCUMENT ME!
491    *
492    * @param node
493    * DOCUMENT ME!
494    * @param tmp
495    * DOCUMENT ME!
496    * @param seqset
497    * DOCUMENT ME!
498    *
499    * @return DOCUMENT ME!
500    */
 
501  29 toggle private static List<SequenceI> _sortByTree(BinaryNode node,
502    List<SequenceI> tmp, List<SequenceI> seqset)
503    {
504  29 if (node == null)
505    {
506  0 return tmp;
507    }
508   
509  29 BinaryNode left = node.left();
510  29 BinaryNode right = node.right();
511   
512  29 if ((left == null) && (right == null))
513    {
514  15 if (!(node instanceof SequenceNode
515    && ((SequenceNode) node).isPlaceholder())
516    && (node.element() != null))
517    {
518  15 if (node.element() instanceof SequenceI)
519    {
520  15 if (!tmp.contains(node.element())) // && (seqset==null ||
521    // seqset.size()==0 ||
522    // seqset.contains(tmp)))
523    {
524  15 tmp.add((SequenceI) node.element());
525    }
526    }
527    }
528   
529  15 return tmp;
530    }
531    else
532    {
533  14 _sortByTree(left, tmp, seqset);
534  14 _sortByTree(right, tmp, seqset);
535    }
536   
537  14 return tmp;
538    }
539   
540    // Ordering Objects
541    // Alignment.sortBy(OrderObj) - sequence of sequence pointer refs in
542    // appropriate order
543    //
544   
545    /**
546    * recover the order of sequences given by the safe numbering scheme introducd
547    * SeqsetUtils.uniquify.
548    */
 
549  8 toggle public static void recoverOrder(SequenceI[] alignment)
550    {
551  8 float[] ids = new float[alignment.length];
552   
553  32 for (int i = 0; i < alignment.length; i++)
554    {
555  24 ids[i] = (Float.valueOf(alignment[i].getName().substring(8)))
556    .floatValue();
557    }
558   
559  8 jalview.util.QuickSort.sort(ids, alignment);
560    }
561   
562    /**
563    * Sort sequence in order of increasing score attribute for annotation with a
564    * particular scoreLabel. Or reverse if same label was used previously
565    *
566    * @param scoreLabel
567    * exact label for sequence associated AlignmentAnnotation scores to
568    * use for sorting.
569    * @param alignment
570    * sequences to be sorted
571    */
 
572  0 toggle public static void sortByAnnotationScore(String scoreLabel,
573    AlignmentI alignment)
574    {
575  0 SequenceI[] seqs = alignment.getSequencesArray();
576  0 boolean[] hasScore = new boolean[seqs.length]; // per sequence score
577    // presence
578  0 int hasScores = 0; // number of scores present on set
579  0 double[] scores = new double[seqs.length];
580  0 double min = 0, max = 0;
581  0 for (int i = 0; i < seqs.length; i++)
582    {
583  0 AlignmentAnnotation[] scoreAnn = seqs[i].getAnnotation(scoreLabel);
584  0 if (scoreAnn != null)
585    {
586  0 hasScores++;
587  0 hasScore[i] = true;
588  0 scores[i] = scoreAnn[0].getScore(); // take the first instance of this
589    // score.
590  0 if (hasScores == 1)
591    {
592  0 max = min = scores[i];
593    }
594    else
595    {
596  0 if (max < scores[i])
597    {
598  0 max = scores[i];
599    }
600  0 if (min > scores[i])
601    {
602  0 min = scores[i];
603    }
604    }
605    }
606    else
607    {
608  0 hasScore[i] = false;
609    }
610    }
611  0 if (hasScores == 0)
612    {
613  0 return; // do nothing - no scores present to sort by.
614    }
615  0 if (hasScores < seqs.length)
616    {
617  0 for (int i = 0; i < seqs.length; i++)
618    {
619  0 if (!hasScore[i])
620    {
621  0 scores[i] = (max + i + 1.0);
622    }
623    }
624    }
625   
626  0 jalview.util.QuickSort.sort(scores, seqs);
627   
628  0 AlignmentSorter as = getInstance();
629   
630  0 if (as.lastSortByAnnotation != scoreLabel)
631    {
632  0 as.lastSortByAnnotation = scoreLabel;
633  0 setOrder(alignment, seqs);
634    }
635    else
636    {
637  0 setReverseOrder(alignment, seqs);
638    }
639    }
640   
641    /**
642    * Sort sequences by feature score or density, optionally restricted by
643    * feature types, feature groups, or alignment start/end positions.
644    * <p>
645    * If the sort is repeated for the same combination of types and groups, sort
646    * order is reversed.
647    *
648    * @param featureTypes
649    * a list of feature types to include (or null for all)
650    * @param groups
651    * a list of feature groups to include (or null for all)
652    * @param startCol
653    * start column position to include (base zero)
654    * @param endCol
655    * end column position to include (base zero)
656    * @param alignment
657    * the alignment to be sorted
658    * @param method
659    * either "average_score" or "density" ("text" not yet implemented)
660    */
 
661  6 toggle public static void sortByFeature(List<String> featureTypes,
662    List<String> groups, final int startCol, final int endCol,
663    AlignmentI alignment, String method)
664    {
665  6 if (method != FEATURE_SCORE && method != FEATURE_LABEL
666    && method != FEATURE_DENSITY)
667    {
668  0 String msg = String.format(
669    "Implementation Error - sortByFeature method must be either '%s' or '%s'",
670    FEATURE_SCORE, FEATURE_DENSITY);
671  0 jalview.bin.Console.errPrintln(msg);
672  0 return;
673    }
674   
675  6 flipFeatureSortIfUnchanged(method, featureTypes, groups, startCol,
676    endCol);
677   
678  6 SequenceI[] seqs = alignment.getSequencesArray();
679   
680  6 boolean[] hasScore = new boolean[seqs.length]; // per sequence score
681    // presence
682  6 int hasScores = 0; // number of scores present on set
683  6 double[] scores = new double[seqs.length];
684  6 int[] seqScores = new int[seqs.length];
685  6 Object[][] feats = new Object[seqs.length][];
686  6 double min = 0d;
687  6 double max = 0d;
688   
689  30 for (int i = 0; i < seqs.length; i++)
690    {
691    /*
692    * get sequence residues overlapping column region
693    * and features for residue positions and specified types
694    */
695  24 String[] types = featureTypes == null ? null
696    : featureTypes.toArray(new String[featureTypes.size()]);
697  24 List<SequenceFeature> sfs = seqs[i].findFeatures(startCol + 1,
698    endCol + 1, types);
699   
700  24 seqScores[i] = 0;
701  24 scores[i] = 0.0;
702   
703  24 Iterator<SequenceFeature> it = sfs.listIterator();
704  52 while (it.hasNext())
705    {
706  28 SequenceFeature sf = it.next();
707   
708    /*
709    * accept all features with null or empty group, otherwise
710    * check group is one of the currently visible groups
711    */
712  28 String featureGroup = sf.getFeatureGroup();
713  28 if (groups != null && featureGroup != null
714    && !"".equals(featureGroup)
715    && !groups.contains(featureGroup))
716    {
717  1 it.remove();
718    }
719    else
720    {
721  27 float score = sf.getScore();
722  27 if (FEATURE_SCORE.equals(method) && !Float.isNaN(score))
723    {
724  21 if (seqScores[i] == 0)
725    {
726  15 hasScores++;
727    }
728  21 seqScores[i]++;
729  21 hasScore[i] = true;
730  21 scores[i] += score;
731    // take the first instance of this score // ??
732    }
733    }
734    }
735   
736  24 feats[i] = sfs.toArray(new SequenceFeature[sfs.size()]);
737  24 if (!sfs.isEmpty())
738    {
739  18 if (method == FEATURE_LABEL)
740    {
741    // order the labels by alphabet (not yet implemented)
742  0 String[] labs = new String[sfs.size()];
743  0 for (int l = 0; l < sfs.size(); l++)
744    {
745  0 SequenceFeature sf = sfs.get(l);
746  0 String description = sf.getDescription();
747  0 labs[l] = (description != null ? description : sf.getType());
748    }
749  0 QuickSort.sort(labs, feats[i]);
750    }
751    }
752  24 if (hasScore[i])
753    {
754    // compute average score
755  15 scores[i] /= seqScores[i];
756    // update the score bounds.
757  15 if (hasScores == 1)
758    {
759  5 min = scores[i];
760  5 max = min;
761    }
762    else
763    {
764  10 max = Math.max(max, scores[i]);
765  10 min = Math.min(min, scores[i]);
766    }
767    }
768    }
769   
770  6 boolean doSort = false;
771   
772  6 if (FEATURE_SCORE.equals(method))
773    {
774  6 if (hasScores == 0)
775    {
776  1 return; // do nothing - no scores present to sort by.
777    }
778    // pad score matrix
779  5 if (hasScores < seqs.length)
780    {
781  25 for (int i = 0; i < seqs.length; i++)
782    {
783  20 if (!hasScore[i])
784    {
785  5 scores[i] = (max + 1 + i);
786    }
787    else
788    {
789    // int nf = (feats[i] == null) ? 0
790    // : ((SequenceFeature[]) feats[i]).length;
791    // // jalview.bin.Console.errPrintln("Sorting on Score: seq " +
792    // seqs[i].getName()
793    // + " Feats: " + nf + " Score : " + scores[i]);
794    }
795    }
796    }
797  5 doSort = true;
798    }
799  0 else if (FEATURE_DENSITY.equals(method))
800    {
801  0 for (int i = 0; i < seqs.length; i++)
802    {
803  0 int featureCount = feats[i] == null ? 0
804    : ((SequenceFeature[]) feats[i]).length;
805  0 scores[i] = featureCount;
806    // jalview.bin.Console.errPrintln("Sorting on Density: seq
807    // "+seqs[i].getName()+
808    // " Feats: "+featureCount+" Score : "+scores[i]);
809    }
810  0 doSort = true;
811    }
812  5 if (doSort)
813    {
814  5 QuickSort.sortByDouble(scores, seqs,
815    getInstance().sortByFeatureAscending);
816    }
817  5 setOrder(alignment, seqs);
818    }
819   
820    /**
821    * Builds a string hash of criteria for sorting, and if unchanged from last
822    * time, reverse the sort order
823    *
824    * @param method
825    * @param featureTypes
826    * @param groups
827    * @param startCol
828    * @param endCol
829    */
 
830  6 toggle protected static void flipFeatureSortIfUnchanged(String method,
831    List<String> featureTypes, List<String> groups,
832    final int startCol, final int endCol)
833    {
834  6 StringBuilder sb = new StringBuilder(64);
835  6 sb.append(startCol).append(method).append(endCol);
836  6 if (featureTypes != null)
837    {
838  2 Collections.sort(featureTypes);
839  2 sb.append(featureTypes.toString());
840    }
841  6 if (groups != null)
842    {
843  1 Collections.sort(groups);
844  1 sb.append(groups.toString());
845    }
846  6 String scoreCriteria = sb.toString();
847   
848    /*
849    * if resorting on the same criteria, toggle sort order
850    */
851  6 AlignmentSorter as = getInstance();
852  6 if (as.sortByFeatureCriteria == null
853    || !scoreCriteria.equals(as.sortByFeatureCriteria))
854    {
855  4 as.sortByFeatureAscending = true;
856    }
857    else
858    {
859  2 as.sortByFeatureAscending = !as.sortByFeatureAscending;
860    }
861  6 as.sortByFeatureCriteria = scoreCriteria;
862    }
863   
864    /**
865    * Set the alignment's sequences list to contain the sequences from a
866    * temporary list, first adding all the elements from the tmp list, then adding all sequences in the alignment that
867    * are not in the list. Option to do the final sort either in order or in reverse order.
868    *
869    * @param align The alignment being sorted
870    * @param tmp
871    * the temporary sequence list
872    * @param ascending
873    * false for reversed order; only sequences already in
874    * the alignment will be used (which is actually already guaranteed
875    * by vectorSubsetToArray)
876    */
 
877  5 toggle private static void set(AlignmentI align, List<SequenceI> tmp,
878    boolean ascending)
879    {
880  5 set(align, vectorSubsetToArray(align.getSequences(), tmp), ascending);
881    }
882   
883    /**
884    * Set the alignment's sequences list to contain these sequences, either in
885    * this order or its reverse.
886    *
887    * @param align
888    * @param seqs
889    * the new sequence array
890    * @param ascending
891    * false for reversed order; if ascending, only sequences already in
892    * the alignment will be used; if descending, then a direct 1:1
893    * replacement is made
894    */
 
895  5 toggle private static void set(AlignmentI align, SequenceI[] seqs,
896    boolean ascending)
897    {
898  5 if (ascending)
899    {
900  3 setOrder(align, seqs);
901    }
902    else
903    {
904  2 setReverseOrder(align, seqs);
905    }
906   
907    }
908   
909    /**
910    * Replace the alignment's sequences with values in an array, clearing the
911    * alignment's sequence list and filtering for sequences that are actually in
912    * the alignment already.
913    *
914    * @param align
915    * the Alignment
916    * @param seqs
917    * the array of replacement values, of any length
918    */
 
919  9 toggle public static void setOrder(AlignmentI align, SequenceI[] seqs)
920    {
921    // NOTE: DO NOT USE align.setSequenceAt() here - it will NOT work
922  9 List<SequenceI> seqList = align.getSequences();
923  9 synchronized (seqList)
924    {
925  9 List<SequenceI> tmp = new ArrayList<>();
926   
927  125 for (int i = 0; i < seqs.length; i++)
928    {
929  116 if (seqList.contains(seqs[i]))
930    {
931  116 tmp.add(seqs[i]);
932    }
933    }
934   
935  9 seqList.clear();
936    // User may have hidden seqs, then clicked undo or redo
937  125 for (int i = 0; i < tmp.size(); i++)
938    {
939  116 seqList.add(tmp.get(i));
940    }
941    }
942    }
943   
944    /**
945    * Replace the alignment's sequences or a subset of those sequences with
946    * values in an array in reverse order. All sequences are replaced; no check
947    * is made that these sequences are in the alignment already.
948    *
949    * @param align
950    * the Alignment
951    * @param seqs
952    * the array of replacement values, length must be less than or equal
953    * to Alignment.sequences.size()
954    */
 
955  2 toggle private static void setReverseOrder(AlignmentI align, SequenceI[] seqs)
956    {
957  2 int nSeq = seqs.length;
958   
959  2 int len = (nSeq + (nSeq % 2)) / 2;
960    // int len = 0;
961    //
962    // if ((nSeq % 2) == 0)
963    // {
964    // len = nSeq / 2;
965    // }
966    // else
967    // {
968    // len = (nSeq + 1) / 2;
969    // }
970   
971    // NOTE: DO NOT USE align.setSequenceAt() here - it will NOT work
972  2 List<SequenceI> seqList = align.getSequences();
973  2 synchronized (seqList)
974    {
975  36 for (int i = 0; i < len; i++)
976    {
977    // SequenceI tmp = seqs[i];
978  34 seqList.set(i, seqs[nSeq - i - 1]);
979  34 seqList.set(nSeq - i - 1, seqs[i]);
980    }
981    }
982    }
983   
984    /**
985    * Create and array of reordered sequences in order first from tmp that are
986    * present in seqList already, then, after that, any remaining sequences in
987    * seqList not in tmp. Any sequences in tmp that are not in seqList already
988    * are discarded.
989    *
990    * @param seqList
991    * thread safe collection of sequences originally in the alignment
992    * @param tmp
993    * thread safe collection of sequences or subsequences possibly in
994    * seqList
995    *
996    * @return intersect(tmp,seqList)+intersect(complement(tmp),seqList)
997    */
 
998  5 toggle private static SequenceI[] vectorSubsetToArray(List<SequenceI> seqList,
999    List<SequenceI> tmp)
1000    {
1001  5 ArrayList<SequenceI> seqs = new ArrayList<>();
1002  5 int n = seqList.size();
1003  5 BitSet bs = new BitSet(n);
1004  5 bs.set(0, n);
1005  152 for (int i = 0, nt = tmp.size(); i < nt; i++)
1006    {
1007  147 SequenceI sq = tmp.get(i);
1008  147 int idx = seqList.indexOf(sq);
1009  147 if (idx >= 0 && bs.get(idx))
1010    {
1011  147 seqs.add(sq);
1012  147 bs.clear(idx);
1013    }
1014    }
1015   
1016  5 for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1))
1017    {
1018  0 seqs.add(seqList.get(i));
1019    }
1020   
1021  5 return seqs.toArray(new SequenceI[seqs.size()]);
1022    }
1023   
1024    }