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