Clover icon

Coverage Report

  1. Project Clover database Tue Nov 18 2025 10:51:49 GMT
  2. Package jalview.analysis

File AlignmentSorter.java

 

Coverage histogram

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

Code metrics

150
250
18
1
940
572
104
0.42
13.89
18
5.78

Classes

Class Line # Actions
AlignmentSorter 55 250 104
0.5287081652.9%
 

Contributing tests

This file is covered by 1 test. .

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