da2c02b8bee636476e6f8309a833549e7e196424
[jalview.git] / src / jalview / gui / structurechooser / PDBStructureChooserQuerySource.java
1 package jalview.gui.structurechooser;
2
3 import java.util.Locale;
4
5 import java.util.ArrayList;
6 import java.util.Collection;
7 import java.util.HashSet;
8 import java.util.LinkedHashSet;
9 import java.util.List;
10 import java.util.Objects;
11 import java.util.Set;
12
13 import javax.swing.JTable;
14 import javax.swing.table.TableModel;
15
16 import jalview.datamodel.DBRefEntry;
17 import jalview.datamodel.DBRefSource;
18 import jalview.datamodel.PDBEntry;
19 import jalview.datamodel.SequenceI;
20 import jalview.fts.api.FTSData;
21 import jalview.fts.api.FTSDataColumnI;
22 import jalview.fts.api.FTSRestClientI;
23 import jalview.fts.core.FTSDataColumnPreferences;
24 import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
25 import jalview.fts.core.FTSRestRequest;
26 import jalview.fts.core.FTSRestResponse;
27 import jalview.fts.service.pdb.PDBFTSRestClient;
28 import jalview.jbgui.FilterOption;
29 import jalview.util.MessageManager;
30
31 /**
32  * logic for querying the PDBe API for structures of sequences
33  * 
34  * @author jprocter
35  */
36 public class PDBStructureChooserQuerySource
37         extends StructureChooserQuerySource
38 {
39
40   private static int MAX_QLENGTH = 7820;
41
42   protected FTSRestRequest lastPdbRequest;
43
44   protected FTSRestClientI pdbRestClient;
45
46   public PDBStructureChooserQuerySource()
47   {
48     pdbRestClient = PDBFTSRestClient.getInstance();
49     docFieldPrefs = new FTSDataColumnPreferences(
50             PreferenceSource.STRUCTURE_CHOOSER,
51             PDBFTSRestClient.getInstance());
52
53   }
54
55   /**
56    * Builds a query string for a given sequences using its DBRef entries
57    * 
58    * @param seq
59    *          the sequences to build a query for
60    * @return the built query string
61    */
62
63   public String buildQuery(SequenceI seq)
64   {
65     boolean isPDBRefsFound = false;
66     boolean isUniProtRefsFound = false;
67     StringBuilder queryBuilder = new StringBuilder();
68     Set<String> seqRefs = new LinkedHashSet<>();
69
70     /*
71      * note PDBs as DBRefEntry so they are not duplicated in query
72      */
73     Set<String> pdbids = new HashSet<>();
74
75     if (seq.getAllPDBEntries() != null
76             && queryBuilder.length() < MAX_QLENGTH)
77     {
78       for (PDBEntry entry : seq.getAllPDBEntries())
79       {
80         if (isValidSeqName(entry.getId()))
81         {
82           String id = entry.getId().toLowerCase(Locale.ROOT);
83           queryBuilder.append("pdb_id:").append(id).append(" OR ");
84           isPDBRefsFound = true;
85           pdbids.add(id);
86         }
87       }
88     }
89
90     List<DBRefEntry> refs = seq.getDBRefs();
91     if (refs != null && refs.size() != 0)
92     {
93       for (int ib = 0, nb = refs.size(); ib < nb; ib++)
94       {
95         DBRefEntry dbRef = refs.get(ib);
96         if (isValidSeqName(getDBRefId(dbRef))
97                 && queryBuilder.length() < MAX_QLENGTH)
98         {
99           if (dbRef.getSource().equalsIgnoreCase(DBRefSource.UNIPROT))
100           {
101             queryBuilder.append("uniprot_accession:")
102                     .append(getDBRefId(dbRef)).append(" OR ");
103             queryBuilder.append("uniprot_id:").append(getDBRefId(dbRef))
104                     .append(" OR ");
105             isUniProtRefsFound = true;
106           }
107           else if (dbRef.getSource().equalsIgnoreCase(DBRefSource.PDB))
108           {
109
110             String id = getDBRefId(dbRef).toLowerCase(Locale.ROOT);
111             if (!pdbids.contains(id))
112             {
113               queryBuilder.append("pdb_id:").append(id).append(" OR ");
114               isPDBRefsFound = true;
115               pdbids.add(id);
116             }
117           }
118           else
119           {
120             seqRefs.add(getDBRefId(dbRef));
121           }
122         }
123       }
124     }
125
126     if (!isPDBRefsFound && !isUniProtRefsFound)
127     {
128       String seqName = seq.getName();
129       seqName = sanitizeSeqName(seqName);
130       String[] names = seqName.toLowerCase(Locale.ROOT).split("\\|");
131       for (String name : names)
132       {
133         // System.out.println("Found name : " + name);
134         name.trim();
135         if (isValidSeqName(name))
136         {
137           seqRefs.add(name);
138         }
139       }
140
141       for (String seqRef : seqRefs)
142       {
143         queryBuilder.append("text:").append(seqRef).append(" OR ");
144       }
145     }
146
147     int endIndex = queryBuilder.lastIndexOf(" OR ");
148     if (queryBuilder.toString().length() < 6)
149     {
150       return null;
151     }
152     String query = queryBuilder.toString().substring(0, endIndex);
153     return query;
154   }
155
156   /**
157    * Remove the following special characters from input string +, -, &, !, (, ),
158    * {, }, [, ], ^, ", ~, *, ?, :, \
159    * 
160    * @param seqName
161    * @return
162    */
163   public static String sanitizeSeqName(String seqName)
164   {
165     Objects.requireNonNull(seqName);
166     return seqName.replaceAll("\\[\\d*\\]", "")
167             .replaceAll("[^\\dA-Za-z|_]", "").replaceAll("\\s+", "+");
168   }
169
170   /**
171    * Ensures sequence ref names are not less than 3 characters and does not
172    * contain a database name
173    * 
174    * @param seqName
175    * @return
176    */
177   static boolean isValidSeqName(String seqName)
178   {
179     // System.out.println("seqName : " + seqName);
180     String ignoreList = "pdb,uniprot,swiss-prot";
181     if (seqName.length() < 3)
182     {
183       return false;
184     }
185     if (seqName.contains(":"))
186     {
187       return false;
188     }
189     seqName = seqName.toLowerCase(Locale.ROOT);
190     for (String ignoredEntry : ignoreList.split(","))
191     {
192       if (seqName.contains(ignoredEntry))
193       {
194         return false;
195       }
196     }
197     return true;
198   }
199
200   static String getDBRefId(DBRefEntry dbRef)
201   {
202     String ref = dbRef.getAccessionId().replaceAll("GO:", "");
203     return ref;
204   }
205
206   /**
207    * FTSRestClient specific query builder to recover associated structure data
208    * records for a sequence
209    * 
210    * @param seq
211    *          - seq to generate a query for
212    * @param wantedFields
213    *          - fields to retrieve
214    * @param selectedFilterOpt
215    *          - criterion for ranking results (e.g. resolution)
216    * @param b
217    *          - sort ascending or descending
218    * @return
219    * @throws Exception
220    */
221   public FTSRestResponse fetchStructuresMetaData(SequenceI seq,
222           Collection<FTSDataColumnI> wantedFields,
223           FilterOption selectedFilterOpt, boolean b) throws Exception
224   {
225     FTSRestResponse resultList;
226     FTSRestRequest pdbRequest = new FTSRestRequest();
227     pdbRequest.setAllowEmptySeq(false);
228     pdbRequest.setResponseSize(500);
229     pdbRequest.setFieldToSearchBy("(");
230     pdbRequest.setFieldToSortBy(selectedFilterOpt.getValue(), b);
231     pdbRequest.setWantedFields(wantedFields);
232     pdbRequest.setSearchTerm(buildQuery(seq) + ")");
233     pdbRequest.setAssociatedSequence(seq);
234     resultList = pdbRestClient.executeRequest(pdbRequest);
235
236     lastPdbRequest = pdbRequest;
237     return resultList;
238   }
239
240   public List<FilterOption> getAvailableFilterOptions(String VIEWS_FILTER)
241   {
242     List<FilterOption> filters = new ArrayList<FilterOption>();
243     filters.add(new FilterOption(
244             "PDBe " + MessageManager.getString("label.best_quality"),
245             "overall_quality", VIEWS_FILTER, false, this));
246     filters.add(new FilterOption(
247             "PDBe " + MessageManager.getString("label.best_resolution"),
248             "resolution", VIEWS_FILTER, false, this));
249     filters.add(new FilterOption(
250             "PDBe " + MessageManager.getString("label.most_protein_chain"),
251             "number_of_protein_chains", VIEWS_FILTER, false, this));
252     filters.add(new FilterOption(
253             "PDBe " + MessageManager
254                     .getString("label.most_bound_molecules"),
255             "number_of_bound_molecules", VIEWS_FILTER, false, this));
256     filters.add(new FilterOption(
257             "PDBe " + MessageManager
258                     .getString("label.most_polymer_residues"),
259             "number_of_polymer_residues", VIEWS_FILTER, true, this));
260
261     return filters;
262   }
263
264   @Override
265   public boolean needsRefetch(FilterOption selectedFilterOpt)
266   {
267     // PDBe queries never need a refetch first
268     return false;
269   }
270
271   /**
272    * FTSRestClient specific query builder to pick top ranked entry from a
273    * fetchStructuresMetaData query
274    * 
275    * @param seq
276    *          - seq to generate a query for
277    * @param wantedFields
278    *          - fields to retrieve
279    * @param selectedFilterOpt
280    *          - criterion for ranking results (e.g. resolution)
281    * @param b
282    *          - sort ascending or descending
283    * @return
284    * @throws Exception
285    */
286   public FTSRestResponse selectFirstRankedQuery(SequenceI seq,
287           Collection<FTSData> collectedResults,
288           Collection<FTSDataColumnI> wantedFields, String fieldToFilterBy,
289           boolean b) throws Exception
290   {
291
292     FTSRestResponse resultList;
293     FTSRestRequest pdbRequest = new FTSRestRequest();
294     if (fieldToFilterBy.equalsIgnoreCase("uniprot_coverage"))
295     {
296       pdbRequest.setAllowEmptySeq(false);
297       pdbRequest.setResponseSize(1);
298       pdbRequest.setFieldToSearchBy("(");
299       pdbRequest.setSearchTerm(buildQuery(seq) + ")");
300       pdbRequest.setWantedFields(wantedFields);
301       pdbRequest.setAssociatedSequence(seq);
302       pdbRequest.setFacet(true);
303       pdbRequest.setFacetPivot(fieldToFilterBy + ",entry_entity");
304       pdbRequest.setFacetPivotMinCount(1);
305     }
306     else
307     {
308       pdbRequest.setAllowEmptySeq(false);
309       pdbRequest.setResponseSize(1);
310       pdbRequest.setFieldToSearchBy("(");
311       pdbRequest.setFieldToSortBy(fieldToFilterBy, b);
312       pdbRequest.setSearchTerm(buildQuery(seq) + ")");
313       pdbRequest.setWantedFields(wantedFields);
314       pdbRequest.setAssociatedSequence(seq);
315     }
316     resultList = pdbRestClient.executeRequest(pdbRequest);
317
318     lastPdbRequest = pdbRequest;
319     return resultList;
320   }
321
322   @Override
323   public PDBEntry[] collectSelectedRows(JTable restable, int[] selectedRows,
324           List<SequenceI> selectedSeqsToView)
325   {
326     int refSeqColIndex = restable.getColumn("Ref Sequence").getModelIndex();
327
328     PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
329     int count = 0;
330     int idColumnIndex = -1;
331     boolean fromTDB = true;
332     idColumnIndex = restable.getColumn("PDB Id").getModelIndex();
333
334     for (int row : selectedRows)
335     {
336
337       String pdbIdStr = restable.getValueAt(row, idColumnIndex).toString();
338       SequenceI selectedSeq = (SequenceI) restable.getValueAt(row,
339               refSeqColIndex);
340       selectedSeqsToView.add(selectedSeq);
341       PDBEntry pdbEntry = selectedSeq.getPDBEntry(pdbIdStr);
342       if (pdbEntry == null)
343       {
344         pdbEntry = getFindEntry(pdbIdStr, selectedSeq.getAllPDBEntries());
345       }
346
347       if (pdbEntry == null)
348       {
349         pdbEntry = new PDBEntry();
350         pdbEntry.setId(pdbIdStr);
351         pdbEntry.setType(PDBEntry.Type.MMCIF);
352         selectedSeq.getDatasetSequence().addPDBId(pdbEntry);
353       }
354       pdbEntriesToView[count++] = pdbEntry;
355     }
356     return pdbEntriesToView;
357   }
358
359   @Override
360   protected FTSRestRequest getLastFTSRequest()
361   {
362     return lastPdbRequest;
363   }
364
365   public FTSRestResponse executePDBFTSRestRequest(FTSRestRequest pdbRequest)
366           throws Exception
367   {
368     return pdbRestClient.executeRequest(pdbRequest);
369   }
370
371 }