Clover icon

jalviewX

  1. Project Clover database Wed Oct 31 2018 15:13:58 GMT
  2. Package jalview.analysis

File AlignmentSorter.java

 

Coverage histogram

../../img/srcFileCovDistChart5.png
40% of files have more coverage

Code metrics

144
239
17
1
898
544
99
0.41
14.06
17
5.82

Classes

Class Line # Actions
AlignmentSorter 54 239 99 214
0.46546.5%
 

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