JAL-3691 automatic insertion of Locale.ROOT to toUpperCase() and toLowerCase() and...
[jalview.git] / src / jalview / gui / structurechooser / TDBResultAnalyser.java
1 package jalview.gui.structurechooser;
2
3 import java.util.Locale;
4
5 import java.util.ArrayList;
6 import java.util.Arrays;
7 import java.util.BitSet;
8 import java.util.Collection;
9 import java.util.Collections;
10 import java.util.Comparator;
11 import java.util.List;
12
13 import jalview.datamodel.SequenceI;
14 import jalview.fts.api.FTSData;
15 import jalview.fts.core.FTSRestRequest;
16
17 public class TDBResultAnalyser
18 {
19
20   /**
21    * model categories - update as needed. warnings output if unknown types
22    * encountered.
23    * 
24    * Order denotes 'trust'
25    */
26   private static List<String> EXP_CATEGORIES = Arrays
27           .asList(new String[]
28           { "EXPERIMENTALLY DETERMINED", "DEEP-LEARNING", "TEMPLATE-BASED",
29               "AB-INITIO", "CONFORMATIONAL ENSEMBLE" });
30
31   private SequenceI seq;
32
33   private Collection<FTSData> collectedResults;
34
35   private FTSRestRequest lastTdbRequest;
36
37   private int idx_ups;
38
39   private int idx_upe;
40
41   private int idx_mcat;
42
43   private int idx_mqual;
44
45   private int idx_resol;
46
47   /**
48    * selection model
49    */
50   private String filter = null;
51
52   /**
53    * limit to particular source
54    */
55   private String sourceFilter = null;
56
57   private int idx_mprov;
58
59   public TDBResultAnalyser(SequenceI seq,
60           Collection<FTSData> collectedResults,
61           FTSRestRequest lastTdbRequest, String fieldToFilterBy,
62           String string)
63   {
64     this.seq = seq;
65     this.collectedResults = collectedResults;
66     this.lastTdbRequest = lastTdbRequest;
67     this.filter = fieldToFilterBy;
68     this.sourceFilter = string;
69     idx_ups = lastTdbRequest.getFieldIndex("Uniprot Start");
70     idx_upe = lastTdbRequest.getFieldIndex("Uniprot End");
71     idx_mcat = lastTdbRequest.getFieldIndex("Model Category");
72     idx_mprov = lastTdbRequest.getFieldIndex("Provider");
73     idx_mqual = lastTdbRequest.getFieldIndex("Confidence");
74     idx_resol = lastTdbRequest.getFieldIndex("Resolution");
75   }
76
77   /**
78    * maintain and resolve categories to 'trust order' TODO: change the trust
79    * scheme to something comprehensible.
80    * 
81    * @param cat
82    * @return 0 for null cat, less than zero for others
83    */
84   public final int scoreCategory(String cat)
85   {
86     if (cat == null)
87     {
88       return 0;
89     }
90     String upper_cat = cat.toUpperCase(Locale.ROOT);
91     int idx = EXP_CATEGORIES.indexOf(upper_cat);
92     if (idx == -1)
93     {
94       System.out.println("Unknown category: '" + cat + "'");
95       EXP_CATEGORIES.add(upper_cat);
96       idx = EXP_CATEGORIES.size() - 1;
97     }
98     return -EXP_CATEGORIES.size() - idx;
99   }
100
101   /**
102    * sorts records discovered by 3D beacons and excludes any that don't
103    * intersect with the sequence's start/end rage
104    * 
105    * @return
106    */
107   public List<FTSData> getFilteredResponse()
108   {
109     List<FTSData> filteredResponse = new ArrayList<FTSData>();
110
111     // ignore anything outside the sequence region
112     for (FTSData row : collectedResults)
113     {
114       int up_s = (Integer) row.getSummaryData()[idx_ups];
115       int up_e = (Integer) row.getSummaryData()[idx_upe];
116       String provider = (String) row.getSummaryData()[idx_mprov];
117       String mcat = (String) row.getSummaryData()[idx_mcat];
118       // this makes sure all new categories are in the score array.
119       int scorecat = scoreCategory(mcat); 
120       if (sourceFilter == null ||  sourceFilter.equals(provider))
121       {
122         if (seq == row.getSummaryData()[0] && up_e > seq.getStart()
123                 && up_s < seq.getEnd())
124         {
125           filteredResponse.add(row);
126         }
127       }
128     }
129     // sort according to decreasing length,
130     // increasing start
131     Collections.sort(filteredResponse, new Comparator<FTSData>()
132     {
133       @Override
134       public int compare(FTSData o1, FTSData o2)
135       {
136         Object[] o1data = o1.getSummaryData();
137         Object[] o2data = o2.getSummaryData();
138         int o1_s = (Integer) o1data[idx_ups];
139         int o1_e = (Integer) o1data[idx_upe];
140         int o1_cat = scoreCategory((String) o1data[idx_mcat]);
141         String o1_prov= ((String) o1data[idx_mprov]).toUpperCase(Locale.ROOT);
142         int o2_s = (Integer) o2data[idx_ups];
143         int o2_e = (Integer) o2data[idx_upe];
144         int o2_cat = scoreCategory((String) o2data[idx_mcat]);
145         String o2_prov= ((String) o2data[idx_mprov]).toUpperCase(Locale.ROOT);
146         
147
148         if (o1_cat == o2_cat)
149         {
150           if (o1_s == o2_s)
151           {
152             int o1_xtent = o1_e - o1_s;
153             int o2_xtent = o2_e - o2_s;
154             if (o1_xtent == o2_xtent)
155             {
156               if (o1_cat == scoreCategory(EXP_CATEGORIES.get(0)))
157               {
158                 if (o1_prov.equals(o2_prov)) {
159                   if ("PDBE".equals(o1_prov)) {
160                     if (eitherNull(idx_resol,o1data,o2data))
161                     {
162                        return nonNullFirst(idx_resol,o1data,o2data);
163                     }
164                 // experimental structures, so rank on quality
165                 double o1_res = (Double) o1data[idx_resol];
166                 double o2_res = (Double) o2data[idx_resol];
167                 return (o2_res < o1_res) ? 1 : (o2_res == o1_res) ? 0 : -1;
168                 } else {
169                   return 0; // no change in order
170                 }
171               } else {
172                 // PDBe always ranked above all other experimentally determined categories
173                 return "PDBE".equals(o1_prov) ? -1 : "PDBE".equals(o2_prov) ? 1 : 0;
174               }
175               }
176               else
177               {
178                 if (eitherNull(idx_mqual,o1data, o2data)) {
179                   return nonNullFirst(idx_mqual, o1data, o2data);
180                 }
181                 // models, so rank on qmean - b
182                 double o1_mq = (Double) o1data[idx_mqual];
183                 double o2_mq = (Double) o2data[idx_mqual];
184                 return (o2_mq < o1_mq) ? 1 : (o2_mq == o1_mq) ? 0 : -1;
185               }
186             }
187             else
188             {
189               return o1_xtent - o2_xtent;
190             }
191           }
192           else
193           {
194             return o1_s - o2_s;
195           }
196         }
197         else
198         {
199           return o2_cat - o1_cat;
200         }
201       }
202
203       private int nonNullFirst(int idx_resol, Object[] o1data,
204               Object[] o2data)
205       {
206           return o1data[idx_resol] == o2data[idx_resol] ? 0: o1data[idx_resol] != null ? -1 : 1; 
207       }
208
209       private boolean eitherNull(int idx_resol, Object[] o1data,
210               Object[] o2data)
211       {
212         return (o1data[idx_resol] == null || o2data[idx_resol]==null);
213       }
214
215       @Override
216       public boolean equals(Object obj)
217       {
218         return super.equals(obj);
219       }
220     });
221     return filteredResponse;
222   }
223
224   /**
225    * return list of structures to be marked as selected for this sequence
226    * according to given criteria
227    * 
228    * @param filteredStructures
229    *          - sorted, filtered structures from getFilteredResponse
230    * 
231    */
232   public List<FTSData> selectStructures(List<FTSData> filteredStructures)
233   {
234     List<FTSData> selected = new ArrayList<FTSData>();
235     BitSet cover = new BitSet();
236     cover.set(seq.getStart(), seq.getEnd());
237     // walk down the list of structures, selecting some to add to selected
238     for (FTSData structure : filteredStructures)
239     {
240       Object[] odata = structure.getSummaryData();
241       int o1_s = (Integer) odata[idx_ups];
242       int o1_e = (Integer) odata[idx_upe];
243       int o1_cat = scoreCategory((String) odata[idx_mcat]);
244       BitSet scover = new BitSet();
245       // measure intersection
246       scover.set(o1_s, o1_e);
247       scover.and(cover);
248       if (scover.cardinality() > 4)
249       {
250         selected.add(structure);
251         // clear the range covered by this structure
252         cover.andNot(scover);
253       }
254     }
255     if (selected.size()==0)
256     {
257       return selected;
258     }
259     // final step is to sort on length - this might help the superposition
260     // process
261     Collections.sort(selected, new Comparator<FTSData>()
262     {
263       @Override
264       public int compare(FTSData o1, FTSData o2)
265       {
266         Object[] o1data = o1.getSummaryData();
267         Object[] o2data = o2.getSummaryData();
268         int o1_xt = ((Integer) o1data[idx_upe])
269                 - ((Integer) o1data[idx_ups]);
270         int o1_cat = scoreCategory((String) o1data[idx_mcat]);
271         int o2_xt = ((Integer) o2data[idx_upe] - (Integer) o2data[idx_ups]);
272         int o2_cat = scoreCategory((String) o2data[idx_mcat]);
273         return o2_xt - o1_xt;
274       }
275     });
276     if (filter.equals(
277             ThreeDBStructureChooserQuerySource.FILTER_FIRST_BEST_COVERAGE))
278     {
279       return selected.subList(0, 1);
280     }
281     return selected;
282   }
283
284 }