787cd6d55163b078d85fd6d84633f758206b7213
[jalview.git] / src / jalview / analysis / CodonComparator.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.analysis;
22
23 import jalview.datamodel.AlignedCodon;
24
25 import java.util.Comparator;
26
27 /**
28  * Implements rules for comparing two aligned codons, i.e. determining whether
29  * they should occupy the same position in a translated protein alignment, or
30  * one or the other should 'follow' (by preceded by a gap).
31  * 
32  * @author gmcarstairs
33  *
34  */
35 public final class CodonComparator implements Comparator<AlignedCodon>
36 {
37
38   @Override
39   public int compare(AlignedCodon ac1, AlignedCodon ac2)
40   {
41     if (ac1 == null || ac2 == null || ac1.equals(ac2))
42     {
43       return 0;
44     }
45
46     /**
47      * <pre>
48      * Case 1: if one starts before the other, and doesn't end after it, then it
49      * precedes. We ignore the middle base position here.
50      * A--GT
51      * -CT-G
52      * </pre>
53      */
54     if (ac1.pos1 < ac2.pos1 && ac1.pos3 <= ac2.pos3)
55     {
56       return -1;
57     }
58     if (ac2.pos1 < ac1.pos1 && ac2.pos3 <= ac1.pos3)
59     {
60       return 1;
61     }
62
63     /**
64      * <pre>
65      * Case 2: if one ends after the other, and doesn't start before it, then it
66      * follows. We ignore the middle base position here.
67      * -TG-A
68      * G-TC
69      * </pre>
70      */
71     if (ac1.pos3 > ac2.pos3 && ac1.pos1 >= ac2.pos1)
72     {
73       return 1;
74     }
75     if (ac2.pos3 > ac1.pos3 && ac2.pos1 >= ac1.pos1)
76     {
77       return -1;
78     }
79
80     /*
81      * Case 3: if start and end match, compare middle base positions.
82      */
83     if (ac1.pos1 == ac2.pos1 && ac1.pos3 == ac2.pos3)
84     {
85       return Integer.compare(ac1.pos2, ac2.pos2);
86     }
87
88     /*
89      * That just leaves the 'enclosing' case - one codon starts after but ends
90      * before the other. If the middle bases don't match, use their comparison
91      * (majority vote).
92      */
93     int compareMiddles = Integer.compare(ac1.pos2, ac2.pos2);
94     if (compareMiddles != 0)
95     {
96       return compareMiddles;
97     }
98
99     /**
100      * <pre>
101      * Finally just leaves overlap with matching middle base, e.g. 
102      * -A-A-A
103      * G--GG 
104      * In this case the choice is arbitrary whether to compare based on
105      * first or last base position. We pick the first. Note this preserves
106      * symmetricality of the comparison.
107      * </pre>
108      */
109     return Integer.compare(ac1.pos1, ac2.pos1);
110   }
111 }