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 if (alignmentAnnotations == null)
216 // cache 'alignment sequence position' for the annotations
217 saveSequenceIndices(alignmentAnnotations);
219 Comparator<? super AlignmentAnnotation> comparator = getComparator(order);
221 if (alignmentAnnotations != null)
223 synchronized (alignmentAnnotations)
225 Arrays.sort(alignmentAnnotations, comparator);
231 * Calculate and save in a temporary map the position of each annotation's
232 * sequence (if it has one) in the alignment. Faster to do this once than for
233 * every annotation comparison.
235 * @param alignmentAnnotations
237 private void saveSequenceIndices(
238 AlignmentAnnotation[] alignmentAnnotations)
240 sequenceIndices.clear();
241 for (AlignmentAnnotation ann : alignmentAnnotations) {
242 SequenceI seq = ann.sequenceRef;
244 int index = AlignmentUtils.getSequenceIndex(alignment, seq);
245 sequenceIndices.put(seq, index);
251 * Get the comparator for the specified sort order.
256 private Comparator<? super AlignmentAnnotation> getComparator(
257 SequenceAnnotationOrder order)
267 case SEQUENCE_AND_LABEL:
268 return this.bySequenceAndLabel;
269 case LABEL_AND_SEQUENCE:
270 return this.byLabelAndSequence;
272 throw new UnsupportedOperationException(order.toString());
277 * Non-case-sensitive comparison of annotation labels. Returns zero if either
284 private int compareLabels(AlignmentAnnotation o1, AlignmentAnnotation o2)
286 if (o1 == null || o2 == null)
290 String label1 = o1.label;
291 String label2 = o2.label;
292 if (label1 == null && label2 == null)
304 return label1.toUpperCase().compareTo(label2.toUpperCase());
308 * Comparison based on position of associated sequence (if any) in the
309 * alignment. Returns zero if either argument is null.
315 private int compareSequences(AlignmentAnnotation o1,
316 AlignmentAnnotation o2)
318 SequenceI seq1 = o1.sequenceRef;
319 SequenceI seq2 = o2.sequenceRef;
320 if (seq1 == null && seq2 == null)
325 * Sort non-sequence-related before or after sequence-related.
329 return showAutocalcAbove ? -1 : 1;
333 return showAutocalcAbove ? 1 : -1;
335 // get sequence index - but note -1 means 'at end' so needs special handling
336 int index1 = sequenceIndices.get(seq1);
337 int index2 = sequenceIndices.get(seq2);
338 if (index1 == index2)
350 return Integer.compare(index1, index2);