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