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;
11 * A helper class to sort all annotations associated with an alignment in
17 public class AnnotationSorter
20 public enum SequenceAnnotationOrder
22 SEQUENCE_AND_LABEL, LABEL_AND_SEQUENCE, NONE
25 private final AlignmentI alignment;
27 private boolean showAutocalcAbove;
30 * Constructor given an alignment and the location (top or bottom) of
31 * Consensus and similar.
34 * @param showAutocalculatedAbove
36 public AnnotationSorter(AlignmentI alignmentI,
37 boolean showAutocalculatedAbove)
39 this.alignment = alignmentI;
40 this.showAutocalcAbove = showAutocalculatedAbove;
44 * Default comparator sorts as follows by annotation type within sequence
47 * <li>annotations with a reference to a sequence in the alignment are sorted
48 * on sequence ordering</li>
49 * <li>other annotations go 'at the end', with their mutual order unchanged</li>
50 * <li>within the same sequence ref, sort by label (non-case-sensitive)</li>
53 private final Comparator<? super AlignmentAnnotation> bySequenceAndLabel = new Comparator<AlignmentAnnotation>()
56 public int compare(AlignmentAnnotation o1, AlignmentAnnotation o2)
58 if (o1 == null && o2 == null)
72 * Ignore label (keep existing ordering) for
73 * Conservation/Quality/Consensus etc
75 if (o1.sequenceRef == null && o2.sequenceRef == null)
79 int sequenceOrder = compareSequences(o1, o2);
80 return sequenceOrder == 0 ? compareLabels(o1, o2) : sequenceOrder;
85 * This comparator sorts as follows by sequence order within annotation type
87 * <li>annotations with a reference to a sequence in the alignment are sorted
88 * on label (non-case-sensitive)</li>
89 * <li>other annotations go 'at the end', with their mutual order unchanged</li>
90 * <li>within the same label, sort by order of the related sequences</li>
93 private final Comparator<? super AlignmentAnnotation> byLabelAndSequence = new Comparator<AlignmentAnnotation>()
96 public int compare(AlignmentAnnotation o1, AlignmentAnnotation o2)
98 if (o1 == null && o2 == null)
112 * Ignore label (keep existing ordering) for
113 * Conservation/Quality/Consensus etc
115 if (o1.sequenceRef == null && o2.sequenceRef == null)
121 * Sort non-sequence-related before or after sequence-related.
123 if (o1.sequenceRef == null)
125 return showAutocalcAbove ? -1 : 1;
127 if (o2.sequenceRef == null)
129 return showAutocalcAbove ? 1 : -1;
131 int labelOrder = compareLabels(o1, o2);
132 return labelOrder == 0 ? compareSequences(o1, o2) : labelOrder;
137 * noSort leaves sort order unchanged, within sequence- and
138 * non-sequence-related annotations, but may switch the ordering of these
139 * groups. Note this is guaranteed (at least in Java 7) as Arrays.sort() is
140 * guaranteed to be 'stable' (not change ordering of equal items).
142 private Comparator<? super AlignmentAnnotation> noSort = new Comparator<AlignmentAnnotation>()
145 public int compare(AlignmentAnnotation o1, AlignmentAnnotation o2)
147 if (o1 != null && o2 != null)
149 if (o1.sequenceRef == null && o2.sequenceRef != null)
151 return showAutocalcAbove ? -1 : 1;
153 if (o1.sequenceRef != null && o2.sequenceRef == null)
155 return showAutocalcAbove ? 1 : -1;
163 * Sort by the specified ordering of sequence-specific annotations.
165 * @param alignmentAnnotations
168 public void sort(AlignmentAnnotation[] alignmentAnnotations,
169 SequenceAnnotationOrder order)
171 if (order != SequenceAnnotationOrder.NONE)
173 Comparator<? super AlignmentAnnotation> comparator = getComparator(order);
175 if (alignmentAnnotations != null)
177 synchronized (alignmentAnnotations)
179 Arrays.sort(alignmentAnnotations, comparator);
186 * Get the comparator for the specified sort order.
191 private Comparator<? super AlignmentAnnotation> getComparator(
192 SequenceAnnotationOrder order)
202 case SEQUENCE_AND_LABEL:
203 return this.bySequenceAndLabel;
204 case LABEL_AND_SEQUENCE:
205 return this.byLabelAndSequence;
207 throw new UnsupportedOperationException(order.toString());
212 * Non-case-sensitive comparison of annotation labels. Returns zero if either
219 private int compareLabels(AlignmentAnnotation o1, AlignmentAnnotation o2)
221 if (o1 == null || o2 == null)
225 String label1 = o1.label;
226 String label2 = o2.label;
227 if (label1 == null && label2 == null)
239 return label1.toUpperCase().compareTo(label2.toUpperCase());
243 * Comparison based on position of associated sequence (if any) in the
244 * alignment. Returns zero if either argument is null.
250 private int compareSequences(AlignmentAnnotation o1,
251 AlignmentAnnotation o2)
253 SequenceI seq1 = o1.sequenceRef;
254 SequenceI seq2 = o2.sequenceRef;
255 if (seq1 == null && seq2 == null)
260 * Sort non-sequence-related before or after sequence-related.
264 return showAutocalcAbove ? -1 : 1;
268 return showAutocalcAbove ? 1 : -1;
270 // get sequence index - but note -1 means 'at end' so needs special handling
271 int index1 = AlignmentUtils.getSequenceIndex(alignment, seq1);
272 int index2 = AlignmentUtils.getSequenceIndex(alignment, seq2);
273 if (index1 == index2)
285 return Integer.compare(index1, index2);