Clover icon

jalviewX

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

File AnnotationSorter.java

 

Coverage histogram

../../img/srcFileCovDistChart7.png
28% of files have more coverage

Code metrics

80
103
15
2
419
258
68
0.66
6.87
7.5
4.53

Classes

Class Line # Actions
AnnotationSorter 39 97 64 50
0.732620373.3%
AnnotationSorter.SequenceAnnotationOrder 50 6 4 9
0.1818181918.2%
 

Contributing tests

This file is covered by 98 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.datamodel.AlignmentAnnotation;
24    import jalview.datamodel.AlignmentI;
25    import jalview.datamodel.SequenceI;
26   
27    import java.util.Arrays;
28    import java.util.Comparator;
29    import java.util.HashMap;
30    import java.util.Map;
31   
32    /**
33    * A helper class to sort all annotations associated with an alignment in
34    * various ways.
35    *
36    * @author gmcarstairs
37    *
38    */
 
39    public class AnnotationSorter
40    {
41   
42    /**
43    * enum for annotation sort options. The text description is used in the
44    * Preferences drop-down options. The enum name is saved in the preferences
45    * file.
46    *
47    * @author gmcarstairs
48    *
49    */
 
50    public enum SequenceAnnotationOrder
51    {
52    // Text descriptions surface in the Preferences Sort by... options
53    SEQUENCE_AND_LABEL("Sequence"), LABEL_AND_SEQUENCE("Label"),
54    NONE("No sort");
55   
56    private String description;
57   
 
58  3 toggle private SequenceAnnotationOrder(String s)
59    {
60  3 description = s;
61    }
62   
 
63  0 toggle @Override
64    public String toString()
65    {
66  0 return description;
67    }
68   
 
69  0 toggle public static SequenceAnnotationOrder forDescription(String d)
70    {
71  0 for (SequenceAnnotationOrder order : values())
72    {
73  0 if (order.toString().equals(d))
74    {
75  0 return order;
76    }
77    }
78  0 return null;
79    }
80    }
81   
82    // the alignment with respect to which annotations are sorted
83    private final AlignmentI alignment;
84   
85    // user preference for placement of non-sequence annotations
86    private boolean showAutocalcAbove;
87   
88    // working map of sequence index in alignment
89    private final Map<SequenceI, Integer> sequenceIndices = new HashMap<SequenceI, Integer>();
90   
91    /**
92    * Constructor given an alignment and the location (top or bottom) of
93    * Consensus and similar.
94    *
95    * @param alignmentI
96    * @param showAutocalculatedAbove
97    */
 
98  1800 toggle public AnnotationSorter(AlignmentI alignmentI,
99    boolean showAutocalculatedAbove)
100    {
101  1800 this.alignment = alignmentI;
102  1800 this.showAutocalcAbove = showAutocalculatedAbove;
103    }
104   
105    /**
106    * Default comparator sorts as follows by annotation type within sequence
107    * order:
108    * <ul>
109    * <li>annotations with a reference to a sequence in the alignment are sorted
110    * on sequence ordering</li>
111    * <li>other annotations go 'at the end', with their mutual order
112    * unchanged</li>
113    * <li>within the same sequence ref, sort by label (non-case-sensitive)</li>
114    * </ul>
115    */
116    private final Comparator<? super AlignmentAnnotation> bySequenceAndLabel = new Comparator<AlignmentAnnotation>()
117    {
 
118  170268 toggle @Override
119    public int compare(AlignmentAnnotation o1, AlignmentAnnotation o2)
120    {
121  170268 if (o1 == null && o2 == null)
122    {
123  0 return 0;
124    }
125  170268 if (o1 == null)
126    {
127  0 return -1;
128    }
129  170268 if (o2 == null)
130    {
131  0 return 1;
132    }
133   
134    // TODO how to treat sequence-related autocalculated annotation
135  170268 boolean o1auto = o1.autoCalculated && o1.sequenceRef == null;
136  170268 boolean o2auto = o2.autoCalculated && o2.sequenceRef == null;
137    /*
138    * Ignore label (keep existing ordering) for
139    * Conservation/Quality/Consensus etc
140    */
141  170268 if (o1auto && o2auto)
142    {
143  2 return 0;
144    }
145   
146    /*
147    * Sort autocalculated before or after sequence-related.
148    */
149  170266 if (o1auto)
150    {
151  7 return showAutocalcAbove ? -1 : 1;
152    }
153  170259 if (o2auto)
154    {
155  1 return showAutocalcAbove ? 1 : -1;
156    }
157  170258 int sequenceOrder = compareSequences(o1, o2);
158  170258 return sequenceOrder == 0 ? compareLabels(o1, o2) : sequenceOrder;
159    }
160   
 
161  0 toggle @Override
162    public String toString()
163    {
164  0 return "Sort by sequence and label";
165    }
166    };
167   
168    /**
169    * This comparator sorts as follows by sequence order within annotation type
170    * <ul>
171    * <li>annotations with a reference to a sequence in the alignment are sorted
172    * on label (non-case-sensitive)</li>
173    * <li>other annotations go 'at the end', with their mutual order
174    * unchanged</li>
175    * <li>within the same label, sort by order of the related sequences</li>
176    * </ul>
177    */
178    private final Comparator<? super AlignmentAnnotation> byLabelAndSequence = new Comparator<AlignmentAnnotation>()
179    {
 
180  172524 toggle @Override
181    public int compare(AlignmentAnnotation o1, AlignmentAnnotation o2)
182    {
183  172524 if (o1 == null && o2 == null)
184    {
185  0 return 0;
186    }
187  172524 if (o1 == null)
188    {
189  0 return -1;
190    }
191  172524 if (o2 == null)
192    {
193  0 return 1;
194    }
195   
196    // TODO how to treat sequence-related autocalculated annotation
197  172524 boolean o1auto = o1.autoCalculated && o1.sequenceRef == null;
198  172524 boolean o2auto = o2.autoCalculated && o2.sequenceRef == null;
199    /*
200    * Ignore label (keep existing ordering) for
201    * Conservation/Quality/Consensus etc
202    */
203  172524 if (o1auto && o2auto)
204    {
205  2 return 0;
206    }
207   
208    /*
209    * Sort autocalculated before or after sequence-related.
210    */
211  172522 if (o1auto)
212    {
213  7 return showAutocalcAbove ? -1 : 1;
214    }
215  172515 if (o2auto)
216    {
217  1 return showAutocalcAbove ? 1 : -1;
218    }
219  172514 int labelOrder = compareLabels(o1, o2);
220  172514 return labelOrder == 0 ? compareSequences(o1, o2) : labelOrder;
221    }
222   
 
223  0 toggle @Override
224    public String toString()
225    {
226  0 return "Sort by label and sequence";
227    }
228    };
229   
230    /**
231    * noSort leaves sort order unchanged, within sequence- and autocalculated
232    * annotations, but may switch the ordering of these groups. Note this is
233    * guaranteed (at least in Java 7) as Arrays.sort() is guaranteed to be
234    * 'stable' (not change ordering of equal items).
235    */
236    private Comparator<? super AlignmentAnnotation> noSort = new Comparator<AlignmentAnnotation>()
237    {
 
238  10216 toggle @Override
239    public int compare(AlignmentAnnotation o1, AlignmentAnnotation o2)
240    {
241    // TODO how to treat sequence-related autocalculated annotation
242  10216 boolean o1auto = o1.autoCalculated && o1.sequenceRef == null;
243  10216 boolean o2auto = o2.autoCalculated && o2.sequenceRef == null;
244    // TODO skip this test to allow customised ordering of all annotations
245    // - needs a third option: place autocalculated first / last / none
246  10216 if (o1 != null && o2 != null)
247    {
248  10216 if (o1auto && !o2auto)
249    {
250  1190 return showAutocalcAbove ? -1 : 1;
251    }
252  9026 if (!o1auto && o2auto)
253    {
254  173 return showAutocalcAbove ? 1 : -1;
255    }
256    }
257  8853 return 0;
258    }
259   
 
260  0 toggle @Override
261    public String toString()
262    {
263  0 return "No sort";
264    }
265    };
266   
267    /**
268    * Sort by the specified ordering of sequence-specific annotations.
269    *
270    * @param alignmentAnnotations
271    * @param order
272    */
 
273  1806 toggle public void sort(AlignmentAnnotation[] alignmentAnnotations,
274    SequenceAnnotationOrder order)
275    {
276  1806 if (alignmentAnnotations == null)
277    {
278  0 return;
279    }
280    // cache 'alignment sequence position' for the annotations
281  1806 saveSequenceIndices(alignmentAnnotations);
282   
283  1806 Comparator<? super AlignmentAnnotation> comparator = getComparator(
284    order);
285   
286  1806 if (alignmentAnnotations != null)
287    {
288  1806 synchronized (alignmentAnnotations)
289    {
290  1806 Arrays.sort(alignmentAnnotations, comparator);
291    }
292    }
293    }
294   
295    /**
296    * Calculate and save in a temporary map the position of each annotation's
297    * sequence (if it has one) in the alignment. Faster to do this once than for
298    * every annotation comparison.
299    *
300    * @param alignmentAnnotations
301    */
 
302  1806 toggle private void saveSequenceIndices(
303    AlignmentAnnotation[] alignmentAnnotations)
304    {
305  1806 sequenceIndices.clear();
306  1806 for (AlignmentAnnotation ann : alignmentAnnotations)
307    {
308  67317 SequenceI seq = ann.sequenceRef;
309  67317 if (seq != null)
310    {
311  57660 int index = AlignmentUtils.getSequenceIndex(alignment, seq);
312  57660 sequenceIndices.put(seq, index);
313    }
314    }
315    }
316   
317    /**
318    * Get the comparator for the specified sort order.
319    *
320    * @param order
321    * @return
322    */
 
323  1806 toggle private Comparator<? super AlignmentAnnotation> getComparator(
324    SequenceAnnotationOrder order)
325    {
326  1806 if (order == null)
327    {
328  0 return noSort;
329    }
330  1806 switch (order)
331    {
332  1787 case NONE:
333  1787 return this.noSort;
334  8 case SEQUENCE_AND_LABEL:
335  8 return this.bySequenceAndLabel;
336  11 case LABEL_AND_SEQUENCE:
337  11 return this.byLabelAndSequence;
338  0 default:
339  0 throw new UnsupportedOperationException(order.toString());
340    }
341    }
342   
343    /**
344    * Non-case-sensitive comparison of annotation labels. Returns zero if either
345    * argument is null.
346    *
347    * @param o1
348    * @param o2
349    * @return
350    */
 
351  186422 toggle private int compareLabels(AlignmentAnnotation o1, AlignmentAnnotation o2)
352    {
353  186422 if (o1 == null || o2 == null)
354    {
355  0 return 0;
356    }
357  186422 String label1 = o1.label;
358  186422 String label2 = o2.label;
359  186422 if (label1 == null && label2 == null)
360    {
361  0 return 0;
362    }
363  186422 if (label1 == null)
364    {
365  0 return -1;
366    }
367  186422 if (label2 == null)
368    {
369  0 return 1;
370    }
371  186422 return label1.toUpperCase().compareTo(label2.toUpperCase());
372    }
373   
374    /**
375    * Comparison based on position of associated sequence (if any) in the
376    * alignment. Returns zero if either argument is null.
377    *
378    * @param o1
379    * @param o2
380    * @return
381    */
 
382  237524 toggle private int compareSequences(AlignmentAnnotation o1,
383    AlignmentAnnotation o2)
384    {
385  237524 SequenceI seq1 = o1.sequenceRef;
386  237524 SequenceI seq2 = o2.sequenceRef;
387  237524 if (seq1 == null && seq2 == null)
388    {
389  0 return 0;
390    }
391    /*
392    * Sort non-sequence-related before or after sequence-related.
393    */
394  237524 if (seq1 == null)
395    {
396  0 return showAutocalcAbove ? -1 : 1;
397    }
398  237524 if (seq2 == null)
399    {
400  0 return showAutocalcAbove ? 1 : -1;
401    }
402    // get sequence index - but note -1 means 'at end' so needs special handling
403  237524 int index1 = sequenceIndices.get(seq1);
404  237524 int index2 = sequenceIndices.get(seq2);
405  237524 if (index1 == index2)
406    {
407  15852 return 0;
408    }
409  221672 if (index1 == -1)
410    {
411  0 return -1;
412    }
413  221672 if (index2 == -1)
414    {
415  0 return 1;
416    }
417  221672 return Integer.compare(index1, index2);
418    }
419    }