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 Comparator<? super AlignmentAnnotation> comparator = getComparator(order);
173 if (alignmentAnnotations != null)
175 synchronized (alignmentAnnotations)
177 Arrays.sort(alignmentAnnotations, comparator);
183 * Get the comparator for the specified sort order.
188 private Comparator<? super AlignmentAnnotation> getComparator(
189 SequenceAnnotationOrder order)
199 case SEQUENCE_AND_LABEL:
200 return this.bySequenceAndLabel;
201 case LABEL_AND_SEQUENCE:
202 return this.byLabelAndSequence;
204 throw new UnsupportedOperationException(order.toString());
209 * Non-case-sensitive comparison of annotation labels. Returns zero if either
216 private int compareLabels(AlignmentAnnotation o1, AlignmentAnnotation o2)
218 if (o1 == null || o2 == null)
222 String label1 = o1.label;
223 String label2 = o2.label;
224 if (label1 == null && label2 == null)
236 return label1.toUpperCase().compareTo(label2.toUpperCase());
240 * Comparison based on position of associated sequence (if any) in the
241 * alignment. Returns zero if either argument is null.
247 private int compareSequences(AlignmentAnnotation o1,
248 AlignmentAnnotation o2)
250 SequenceI seq1 = o1.sequenceRef;
251 SequenceI seq2 = o2.sequenceRef;
252 if (seq1 == null && seq2 == null)
257 * Sort non-sequence-related before or after sequence-related.
261 return showAutocalcAbove ? -1 : 1;
265 return showAutocalcAbove ? 1 : -1;
267 // get sequence index - but note -1 means 'at end' so needs special handling
268 int index1 = AlignmentUtils.getSequenceIndex(alignment, seq1);
269 int index2 = AlignmentUtils.getSequenceIndex(alignment, seq2);
270 if (index1 == index2)
282 return Integer.compare(index1, index2);