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
21 * enum for annotation sort options. The text description is used in the
22 * Preferences drop-down options. The enum name is saved in the preferences
28 public enum SequenceAnnotationOrder
30 // Text descriptions surface in the Preferences Sort by... options
31 SEQUENCE_AND_LABEL("Sequence"), LABEL_AND_SEQUENCE("Label"), NONE(
34 private String description;
36 private SequenceAnnotationOrder(String s)
42 public String toString()
47 public static SequenceAnnotationOrder forDescription(String d) {
48 for (SequenceAnnotationOrder order : values())
50 if (order.toString().equals(d))
59 private final AlignmentI alignment;
61 private boolean showAutocalcAbove;
64 * Constructor given an alignment and the location (top or bottom) of
65 * Consensus and similar.
68 * @param showAutocalculatedAbove
70 public AnnotationSorter(AlignmentI alignmentI,
71 boolean showAutocalculatedAbove)
73 this.alignment = alignmentI;
74 this.showAutocalcAbove = showAutocalculatedAbove;
78 * Default comparator sorts as follows by annotation type within sequence
81 * <li>annotations with a reference to a sequence in the alignment are sorted
82 * on sequence ordering</li>
83 * <li>other annotations go 'at the end', with their mutual order unchanged</li>
84 * <li>within the same sequence ref, sort by label (non-case-sensitive)</li>
87 private final Comparator<? super AlignmentAnnotation> bySequenceAndLabel = new Comparator<AlignmentAnnotation>()
90 public int compare(AlignmentAnnotation o1, AlignmentAnnotation o2)
92 if (o1 == null && o2 == null)
106 * Ignore label (keep existing ordering) for
107 * Conservation/Quality/Consensus etc
109 if (o1.sequenceRef == null && o2.sequenceRef == null)
113 int sequenceOrder = compareSequences(o1, o2);
114 return sequenceOrder == 0 ? compareLabels(o1, o2) : sequenceOrder;
119 * This comparator sorts as follows by sequence order within annotation type
121 * <li>annotations with a reference to a sequence in the alignment are sorted
122 * on label (non-case-sensitive)</li>
123 * <li>other annotations go 'at the end', with their mutual order unchanged</li>
124 * <li>within the same label, sort by order of the related sequences</li>
127 private final Comparator<? super AlignmentAnnotation> byLabelAndSequence = new Comparator<AlignmentAnnotation>()
130 public int compare(AlignmentAnnotation o1, AlignmentAnnotation o2)
132 if (o1 == null && o2 == null)
146 * Ignore label (keep existing ordering) for
147 * Conservation/Quality/Consensus etc
149 if (o1.sequenceRef == null && o2.sequenceRef == null)
155 * Sort non-sequence-related before or after sequence-related.
157 if (o1.sequenceRef == null)
159 return showAutocalcAbove ? -1 : 1;
161 if (o2.sequenceRef == null)
163 return showAutocalcAbove ? 1 : -1;
165 int labelOrder = compareLabels(o1, o2);
166 return labelOrder == 0 ? compareSequences(o1, o2) : labelOrder;
171 * noSort leaves sort order unchanged, within sequence- and
172 * non-sequence-related annotations, but may switch the ordering of these
173 * groups. Note this is guaranteed (at least in Java 7) as Arrays.sort() is
174 * guaranteed to be 'stable' (not change ordering of equal items).
176 private Comparator<? super AlignmentAnnotation> noSort = new Comparator<AlignmentAnnotation>()
179 public int compare(AlignmentAnnotation o1, AlignmentAnnotation o2)
181 if (o1 != null && o2 != null)
183 if (o1.sequenceRef == null && o2.sequenceRef != null)
185 return showAutocalcAbove ? -1 : 1;
187 if (o1.sequenceRef != null && o2.sequenceRef == null)
189 return showAutocalcAbove ? 1 : -1;
197 * Sort by the specified ordering of sequence-specific annotations.
199 * @param alignmentAnnotations
202 public void sort(AlignmentAnnotation[] alignmentAnnotations,
203 SequenceAnnotationOrder order)
205 Comparator<? super AlignmentAnnotation> comparator = getComparator(order);
207 if (alignmentAnnotations != null)
209 synchronized (alignmentAnnotations)
211 Arrays.sort(alignmentAnnotations, comparator);
217 * Get the comparator for the specified sort order.
222 private Comparator<? super AlignmentAnnotation> getComparator(
223 SequenceAnnotationOrder order)
233 case SEQUENCE_AND_LABEL:
234 return this.bySequenceAndLabel;
235 case LABEL_AND_SEQUENCE:
236 return this.byLabelAndSequence;
238 throw new UnsupportedOperationException(order.toString());
243 * Non-case-sensitive comparison of annotation labels. Returns zero if either
250 private int compareLabels(AlignmentAnnotation o1, AlignmentAnnotation o2)
252 if (o1 == null || o2 == null)
256 String label1 = o1.label;
257 String label2 = o2.label;
258 if (label1 == null && label2 == null)
270 return label1.toUpperCase().compareTo(label2.toUpperCase());
274 * Comparison based on position of associated sequence (if any) in the
275 * alignment. Returns zero if either argument is null.
281 private int compareSequences(AlignmentAnnotation o1,
282 AlignmentAnnotation o2)
284 SequenceI seq1 = o1.sequenceRef;
285 SequenceI seq2 = o2.sequenceRef;
286 if (seq1 == null && seq2 == null)
291 * Sort non-sequence-related before or after sequence-related.
295 return showAutocalcAbove ? -1 : 1;
299 return showAutocalcAbove ? 1 : -1;
301 // get sequence index - but note -1 means 'at end' so needs special handling
302 int index1 = AlignmentUtils.getSequenceIndex(alignment, seq1);
303 int index2 = AlignmentUtils.getSequenceIndex(alignment, seq2);
304 if (index1 == index2)
316 return Integer.compare(index1, index2);