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