package jalview.analysis; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.SequenceI; import java.util.Arrays; import java.util.Comparator; /** * A helper class to sort all annotations associated with an alignment in * various ways. * * @author gmcarstairs * */ public class AnnotationSorter { private final AlignmentI alignment; public AnnotationSorter(AlignmentI alignmentI) { this.alignment = alignmentI; } /** * Default comparator sorts as follows by annotation type within sequence * order: * */ private final Comparator bySequenceAndType = new Comparator() { @Override public int compare(AlignmentAnnotation o1, AlignmentAnnotation o2) { if (o1 == null && o2 == null) { return 0; } if (o1 == null) { return -1; } if (o2 == null) { return 1; } /* * Ignore label (keep existing ordering) for * Conservation/Quality/Consensus etc */ if (o1.sequenceRef == null && o2.sequenceRef == null) { return 0; } int sequenceOrder = compareSequences(o1, o2); return sequenceOrder == 0 ? compareLabels(o1, o2) : sequenceOrder; } }; /** * This comparator sorts as follows by sequence order within annotation type * */ private final Comparator byTypeAndSequence = new Comparator() { @Override public int compare(AlignmentAnnotation o1, AlignmentAnnotation o2) { if (o1 == null && o2 == null) { return 0; } if (o1 == null) { return -1; } if (o2 == null) { return 1; } /* * Ignore label (keep existing ordering) for * Conservation/Quality/Consensus etc */ if (o1.sequenceRef == null && o2.sequenceRef == null) { return 0; } /* * Sort non-sequence-related after sequence-related. */ if (o1.sequenceRef == null) { return 1; } if (o2.sequenceRef == null) { return -1; } int labelOrder = compareLabels(o1, o2); return labelOrder == 0 ? compareSequences(o1, o2) : labelOrder; } }; /** * Sort by annotation type (label), within sequence order. * Non-sequence-related annotations sort to the end. * * @param alignmentAnnotations */ public void sortBySequenceAndType( AlignmentAnnotation[] alignmentAnnotations) { if (alignmentAnnotations != null) { synchronized (alignmentAnnotations) { Arrays.sort(alignmentAnnotations, bySequenceAndType); } } } /** * Sort by sequence order within annotation type (label). Non-sequence-related * annotations sort to the end. * * @param alignmentAnnotations */ public void sortByTypeAndSequence( AlignmentAnnotation[] alignmentAnnotations) { if (alignmentAnnotations != null) { synchronized (alignmentAnnotations) { Arrays.sort(alignmentAnnotations, byTypeAndSequence); } } } /** * Non-case-sensitive comparison of annotation labels. Returns zero if either * argument is null. * * @param o1 * @param o2 * @return */ private int compareLabels(AlignmentAnnotation o1, AlignmentAnnotation o2) { if (o1 == null || o2 == null) { return 0; } String label1 = o1.label; String label2 = o2.label; if (label1 == null && label2 == null) { return 0; } if (label1 == null) { return -1; } if (label2 == null) { return 1; } return label1.toUpperCase().compareTo(label2.toUpperCase()); } /** * Comparison based on position of associated sequence (if any) in the * alignment. Returns zero if either argument is null. * * @param o1 * @param o2 * @return */ private int compareSequences(AlignmentAnnotation o1, AlignmentAnnotation o2) { SequenceI seq1 = o1.sequenceRef; SequenceI seq2 = o2.sequenceRef; if (seq1 == null && seq2 == null) { return 0; } if (seq1 == null) { return 1; } if (seq2 == null) { return -1; } // get sequence index - but note -1 means 'at end' so needs special handling int index1 = AlignmentUtils.getSequenceIndex(alignment, seq1); int index2 = AlignmentUtils.getSequenceIndex(alignment, seq2); if (index1 == index2) { return 0; } if (index1 == -1) { return -1; } if (index2 == -1) { return 1; } return Integer.compare(index1, index2); } }