JAL-3438 spotless for 2.11.2.0
[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])
142                 .toUpperCase(Locale.ROOT);
143         int o2_s = (Integer) o2data[idx_ups];
144         int o2_e = (Integer) o2data[idx_upe];
145         int o2_cat = scoreCategory((String) o2data[idx_mcat]);
146         String o2_prov = ((String) o2data[idx_mprov])
147                 .toUpperCase(Locale.ROOT);
148
149         if (o1_cat == o2_cat)
150         {
151           if (o1_s == o2_s)
152           {
153             int o1_xtent = o1_e - o1_s;
154             int o2_xtent = o2_e - o2_s;
155             if (o1_xtent == o2_xtent)
156             {
157               if (o1_cat == scoreCategory(EXP_CATEGORIES.get(0)))
158               {
159                 if (o1_prov.equals(o2_prov))
160                 {
161                   if ("PDBE".equals(o1_prov))
162                   {
163                     if (eitherNull(idx_resol, o1data, o2data))
164                     {
165                       return nonNullFirst(idx_resol, o1data, o2data);
166                     }
167                     // experimental structures, so rank on quality
168                     double o1_res = (Double) o1data[idx_resol];
169                     double o2_res = (Double) o2data[idx_resol];
170                     return (o2_res < o1_res) ? 1
171                             : (o2_res == o1_res) ? 0 : -1;
172                   }
173                   else
174                   {
175                     return 0; // no change in order
176                   }
177                 }
178                 else
179                 {
180                   // PDBe always ranked above all other experimentally
181                   // determined categories
182                   return "PDBE".equals(o1_prov) ? -1
183                           : "PDBE".equals(o2_prov) ? 1 : 0;
184                 }
185               }
186               else
187               {
188                 if (eitherNull(idx_mqual, o1data, o2data))
189                 {
190                   return nonNullFirst(idx_mqual, o1data, o2data);
191                 }
192                 // models, so rank on qmean - b
193                 double o1_mq = (Double) o1data[idx_mqual];
194                 double o2_mq = (Double) o2data[idx_mqual];
195                 return (o2_mq < o1_mq) ? 1 : (o2_mq == o1_mq) ? 0 : -1;
196               }
197             }
198             else
199             {
200               return o1_xtent - o2_xtent;
201             }
202           }
203           else
204           {
205             return o1_s - o2_s;
206           }
207         }
208         else
209         {
210           return o2_cat - o1_cat;
211         }
212       }
213
214       private int nonNullFirst(int idx_resol, Object[] o1data,
215               Object[] o2data)
216       {
217         return o1data[idx_resol] == o2data[idx_resol] ? 0
218                 : o1data[idx_resol] != null ? -1 : 1;
219       }
220
221       private boolean eitherNull(int idx_resol, Object[] o1data,
222               Object[] o2data)
223       {
224         return (o1data[idx_resol] == null || o2data[idx_resol] == null);
225       }
226
227       @Override
228       public boolean equals(Object obj)
229       {
230         return super.equals(obj);
231       }
232     });
233     return filteredResponse;
234   }
235
236   /**
237    * return list of structures to be marked as selected for this sequence
238    * according to given criteria
239    * 
240    * @param filteredStructures
241    *          - sorted, filtered structures from getFilteredResponse
242    * 
243    */
244   public List<FTSData> selectStructures(List<FTSData> filteredStructures)
245   {
246     List<FTSData> selected = new ArrayList<FTSData>();
247     BitSet cover = new BitSet();
248     cover.set(seq.getStart(), seq.getEnd());
249     // walk down the list of structures, selecting some to add to selected
250     // TODO: could do simple DP - double loop to select largest number of
251     // structures covering largest number of sites
252     for (FTSData structure : filteredStructures)
253     {
254       Object[] odata = structure.getSummaryData();
255       int o1_s = (Integer) odata[idx_ups];
256       int o1_e = (Integer) odata[idx_upe];
257       int o1_cat = scoreCategory((String) odata[idx_mcat]);
258       BitSet scover = new BitSet();
259       // measure intersection
260       scover.set(o1_s, o1_e);
261       scover.and(cover);
262       if (scover.cardinality() > 4)
263       {
264         selected.add(structure);
265         // clear the range covered by this structure
266         cover.andNot(scover);
267       }
268     }
269     if (selected.size() == 0)
270     {
271       return selected;
272     }
273     // final step is to sort on length - this might help the superposition
274     // process
275     Collections.sort(selected, new Comparator<FTSData>()
276     {
277       @Override
278       public int compare(FTSData o1, FTSData o2)
279       {
280         Object[] o1data = o1.getSummaryData();
281         Object[] o2data = o2.getSummaryData();
282         int o1_xt = ((Integer) o1data[idx_upe])
283                 - ((Integer) o1data[idx_ups]);
284         int o1_cat = scoreCategory((String) o1data[idx_mcat]);
285         int o2_xt = ((Integer) o2data[idx_upe] - (Integer) o2data[idx_ups]);
286         int o2_cat = scoreCategory((String) o2data[idx_mcat]);
287         return o2_xt - o1_xt;
288       }
289     });
290     if (filter.equals(
291             ThreeDBStructureChooserQuerySource.FILTER_FIRST_BEST_COVERAGE))
292     {
293       return selected.subList(0, 1);
294     }
295     return selected;
296   }
297
298 }