1 package jalview.analysis;
3 import jalview.datamodel.AlignmentAnnotation;
4 import jalview.datamodel.AlignmentI;
5 import jalview.datamodel.SequenceI;
7 import java.util.Arrays;
8 import java.util.Comparator;
9 import java.util.HashMap;
13 * A helper class to sort all annotations associated with an alignment in
19 public class AnnotationSorter
23 * enum for annotation sort options. The text description is used in the
24 * Preferences drop-down options. The enum name is saved in the preferences
30 public enum SequenceAnnotationOrder
32 // Text descriptions surface in the Preferences Sort by... options
33 SEQUENCE_AND_LABEL("Sequence"), LABEL_AND_SEQUENCE("Label"), NONE(
36 private String description;
38 private SequenceAnnotationOrder(String s)
44 public String toString()
49 public static SequenceAnnotationOrder forDescription(String d) {
50 for (SequenceAnnotationOrder order : values())
52 if (order.toString().equals(d))
61 // the alignment with respect to which annotations are sorted
62 private final AlignmentI alignment;
64 // user preference for placement of non-sequence annotations
65 private boolean showAutocalcAbove;
67 // working map of sequence index in alignment
68 private final Map<SequenceI, Integer> sequenceIndices = new HashMap<SequenceI, Integer>();
71 * Constructor given an alignment and the location (top or bottom) of
72 * Consensus and similar.
75 * @param showAutocalculatedAbove
77 public AnnotationSorter(AlignmentI alignmentI,
78 boolean showAutocalculatedAbove)
80 this.alignment = alignmentI;
81 this.showAutocalcAbove = showAutocalculatedAbove;
85 * Default comparator sorts as follows by annotation type within sequence
88 * <li>annotations with a reference to a sequence in the alignment are sorted
89 * on sequence ordering</li>
90 * <li>other annotations go 'at the end', with their mutual order unchanged</li>
91 * <li>within the same sequence ref, sort by label (non-case-sensitive)</li>
94 private final Comparator<? super AlignmentAnnotation> bySequenceAndLabel = new Comparator<AlignmentAnnotation>()
97 public int compare(AlignmentAnnotation o1, AlignmentAnnotation o2)
99 if (o1 == null && o2 == null)
113 * Ignore label (keep existing ordering) for
114 * Conservation/Quality/Consensus etc
116 if (o1.sequenceRef == null && o2.sequenceRef == null)
120 int sequenceOrder = compareSequences(o1, o2);
121 return sequenceOrder == 0 ? compareLabels(o1, o2) : sequenceOrder;
126 * This comparator sorts as follows by sequence order within annotation type
128 * <li>annotations with a reference to a sequence in the alignment are sorted
129 * on label (non-case-sensitive)</li>
130 * <li>other annotations go 'at the end', with their mutual order unchanged</li>
131 * <li>within the same label, sort by order of the related sequences</li>
134 private final Comparator<? super AlignmentAnnotation> byLabelAndSequence = new Comparator<AlignmentAnnotation>()
137 public int compare(AlignmentAnnotation o1, AlignmentAnnotation o2)
139 if (o1 == null && o2 == null)
153 * Ignore label (keep existing ordering) for
154 * Conservation/Quality/Consensus etc
156 if (o1.sequenceRef == null && o2.sequenceRef == null)
162 * Sort non-sequence-related before or after sequence-related.
164 if (o1.sequenceRef == null)
166 return showAutocalcAbove ? -1 : 1;
168 if (o2.sequenceRef == null)
170 return showAutocalcAbove ? 1 : -1;
172 int labelOrder = compareLabels(o1, o2);
173 return labelOrder == 0 ? compareSequences(o1, o2) : labelOrder;
178 * noSort leaves sort order unchanged, within sequence- and
179 * non-sequence-related annotations, but may switch the ordering of these
180 * groups. Note this is guaranteed (at least in Java 7) as Arrays.sort() is
181 * guaranteed to be 'stable' (not change ordering of equal items).
183 private Comparator<? super AlignmentAnnotation> noSort = new Comparator<AlignmentAnnotation>()
186 public int compare(AlignmentAnnotation o1, AlignmentAnnotation o2)
188 if (o1 != null && o2 != null)
190 if (o1.sequenceRef == null && o2.sequenceRef != null)
192 return showAutocalcAbove ? -1 : 1;
194 if (o1.sequenceRef != null && o2.sequenceRef == null)
196 return showAutocalcAbove ? 1 : -1;
204 * Sort by the specified ordering of sequence-specific annotations.
206 * @param alignmentAnnotations
209 public void sort(AlignmentAnnotation[] alignmentAnnotations,
210 SequenceAnnotationOrder order)
212 // cache 'alignment sequence position' for the annotations
213 saveSequenceIndices(alignmentAnnotations);
215 Comparator<? super AlignmentAnnotation> comparator = getComparator(order);
217 if (alignmentAnnotations != null)
219 synchronized (alignmentAnnotations)
221 Arrays.sort(alignmentAnnotations, comparator);
227 * Calculate and save in a temporary map the position of each annotation's
228 * sequence (if it has one) in the alignment. Faster to do this once than for
229 * every annotation comparison.
231 * @param alignmentAnnotations
233 private void saveSequenceIndices(
234 AlignmentAnnotation[] alignmentAnnotations)
236 sequenceIndices.clear();
237 for (AlignmentAnnotation ann : alignmentAnnotations) {
238 SequenceI seq = ann.sequenceRef;
240 int index = AlignmentUtils.getSequenceIndex(alignment, seq);
241 sequenceIndices.put(seq, index);
247 * Get the comparator for the specified sort order.
252 private Comparator<? super AlignmentAnnotation> getComparator(
253 SequenceAnnotationOrder order)
263 case SEQUENCE_AND_LABEL:
264 return this.bySequenceAndLabel;
265 case LABEL_AND_SEQUENCE:
266 return this.byLabelAndSequence;
268 throw new UnsupportedOperationException(order.toString());
273 * Non-case-sensitive comparison of annotation labels. Returns zero if either
280 private int compareLabels(AlignmentAnnotation o1, AlignmentAnnotation o2)
282 if (o1 == null || o2 == null)
286 String label1 = o1.label;
287 String label2 = o2.label;
288 if (label1 == null && label2 == null)
300 return label1.toUpperCase().compareTo(label2.toUpperCase());
304 * Comparison based on position of associated sequence (if any) in the
305 * alignment. Returns zero if either argument is null.
311 private int compareSequences(AlignmentAnnotation o1,
312 AlignmentAnnotation o2)
314 SequenceI seq1 = o1.sequenceRef;
315 SequenceI seq2 = o2.sequenceRef;
316 if (seq1 == null && seq2 == null)
321 * Sort non-sequence-related before or after sequence-related.
325 return showAutocalcAbove ? -1 : 1;
329 return showAutocalcAbove ? 1 : -1;
331 // get sequence index - but note -1 means 'at end' so needs special handling
332 int index1 = sequenceIndices.get(seq1);
333 int index2 = sequenceIndices.get(seq2);
334 if (index1 == index2)
346 return Integer.compare(index1, index2);