fc196de3e207602aafc71debba35689bafdbde0b
[jalview.git] / CodonComparator.java
1 package jalview.analysis;
2
3 import jalview.datamodel.AlignedCodon;
4
5 import java.util.Comparator;
6
7 /**
8  * Implements rules for comparing two aligned codons, i.e. determining whether
9  * they should occupy the same position in a translated protein alignment, or
10  * one or the other should 'follow' (by preceded by a gap).
11  * 
12  * @author gmcarstairs
13  *
14  */
15 public final class CodonComparator implements Comparator<AlignedCodon>
16 {
17
18   @Override
19   public int compare(AlignedCodon ac1, AlignedCodon ac2)
20   {
21     if (ac1 == null || ac2 == null || ac1.equals(ac2))
22     {
23       return 0;
24     }
25
26     /**
27      * <pre>
28      * Case 1: if one starts before the other, and doesn't end after it, then it
29      * precedes. We ignore the middle base position here.
30      * A--GT
31      * -CT-G
32      * </pre>
33      */
34     if (ac1.pos1 < ac2.pos1 && ac1.pos3 <= ac2.pos3)
35     {
36       return -1;
37     }
38     if (ac2.pos1 < ac1.pos1 && ac2.pos3 <= ac1.pos3)
39     {
40       return 1;
41     }
42
43     /**
44      * <pre>
45      * Case 2: if one ends after the other, and doesn't start before it, then it
46      * follows. We ignore the middle base position here.
47      * -TG-A
48      * G-TC
49      * </pre>
50      */
51     if (ac1.pos3 > ac2.pos3 && ac1.pos1 >= ac2.pos1)
52     {
53       return 1;
54     }
55     if (ac2.pos3 > ac1.pos3 && ac2.pos1 >= ac1.pos1)
56     {
57       return -1;
58     }
59
60     /*
61      * Case 3: if start and end match, compare middle base positions.
62      */
63     if (ac1.pos1 == ac2.pos1 && ac1.pos3 == ac2.pos3)
64     {
65       return Integer.compare(ac1.pos2, ac2.pos2);
66     }
67
68     /*
69      * That just leaves the 'enclosing' case - one codon starts after but ends
70      * before the other. If the middle bases don't match, use their comparison
71      * (majority vote).
72      */
73     int compareMiddles = Integer.compare(ac1.pos2, ac2.pos2);
74     if (compareMiddles != 0)
75     {
76       return compareMiddles;
77     }
78
79     /**
80      * <pre>
81      * Finally just leaves overlap with matching middle base, e.g. 
82      * -A-A-A
83      * G--GG 
84      * In this case the choice is arbitrary whether to compare based on
85      * first or last base position. We pick the first. Note this preserves
86      * symmetricality of the comparison.
87      * </pre>
88      */
89     return Integer.compare(ac1.pos1, ac2.pos1);
90   }
91 }