4b3f5dea989484dc80aa19849f94f900fdbb8175
[jalview.git] / src / jalview / gui / StructureChooser.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3  * Copyright (C) 2014 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
22 package jalview.gui;
23
24 import jalview.datamodel.DBRefEntry;
25 import jalview.datamodel.PDBEntry;
26 import jalview.datamodel.SequenceI;
27 import jalview.jbgui.GStructureChooser;
28 import jalview.util.MessageManager;
29 import jalview.ws.dbsources.PDBRestClient;
30 import jalview.ws.dbsources.PDBRestClient.PDBDocField;
31 import jalview.ws.uimodel.PDBRestRequest;
32 import jalview.ws.uimodel.PDBRestResponse;
33 import jalview.ws.uimodel.PDBRestResponse.PDBResponseSummary;
34
35 import java.awt.event.ItemEvent;
36 import java.util.ArrayList;
37 import java.util.Collection;
38 import java.util.HashSet;
39 import java.util.List;
40 import java.util.Vector;
41
42 import javax.swing.JCheckBox;
43 import javax.swing.JOptionPane;
44 import javax.swing.ListSelectionModel;
45
46 /**
47  * Provides the behaviors for the Structure chooser Panel
48  * 
49  * @author tcnofoegbu
50  *
51  */
52 @SuppressWarnings("serial")
53 public class StructureChooser extends GStructureChooser
54 {
55   private boolean structuresDiscovered = false;
56
57   private SequenceI selectedSequence;
58
59   private SequenceI[] selectedSequences;
60
61   private IProgressIndicator progressIndicator;
62
63   private Collection<PDBResponseSummary> discoveredStructuresSet = new HashSet<PDBResponseSummary>();
64
65   private PDBRestRequest pdbRequest;
66
67   private PDBRestClient pdbRestCleint;
68
69
70   public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
71           AlignmentPanel ap)
72   {
73     this.ap = ap;
74     this.selectedSequence = selectedSeq;
75     this.selectedSequences = selectedSeqs;
76     this.progressIndicator = (ap == null) ? null : ap.alignFrame;
77     init();
78   }
79
80   /**
81    * Initializes parameters used by the Structure Chooser Panel
82    */
83   public void init()
84   {
85     Thread discoverPDBStructuresThread = new Thread(new Runnable()
86     {
87       @Override
88       public void run()
89       {
90         long startTime = System.currentTimeMillis();
91         String msg = MessageManager.getString("status.fetching_db_refs");
92         updateProgressIndicator(msg, startTime);
93         fetchStructuresMetaData();
94         populateFilterComboBox();
95         updateProgressIndicator(null, startTime);
96         mainFrame.setVisible(true);
97         updateCurrentView();
98       }
99     });
100     discoverPDBStructuresThread.start();
101   }
102
103   /**
104    * Updates the progress indicator with the specified message
105    * 
106    * @param message
107    *          displayed message for the operation
108    * @param id
109    *          unique handle for this indicator
110    */
111   public void updateProgressIndicator(String message, long id)
112   {
113     if (progressIndicator != null)
114     {
115       progressIndicator.setProgressBar(message, id);
116     }
117   }
118
119   /**
120    * Retrieve meta-data for all the structure(s) for a given sequence(s) in a
121    * selection group
122    */
123   public void fetchStructuresMetaData()
124   {
125     long startTime = System.currentTimeMillis();
126     List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
127     // wantedFields.add(PDBDocField.MOLECULE_TYPE);
128     wantedFields.add(PDBDocField.PDB_ID);
129     // wantedFields.add(PDBDocField.GENUS);
130     // wantedFields.add(PDBDocField.GENE_NAME);
131     wantedFields.add(PDBDocField.TITLE);
132     pdbRequest = new PDBRestRequest();
133     pdbRequest.setAllowEmptySeq(false);
134     pdbRequest.setResponseSize(500);
135     pdbRequest.setFieldToSearchBy("(text:");
136     pdbRequest.setWantedFields(wantedFields);
137     for (SequenceI seq : selectedSequences)
138     {
139       pdbRequest.setSearchTerm(buildQuery(seq) + ")");
140       pdbRequest.setAssociatedSequence(seq.getName());
141       pdbRestCleint = new PDBRestClient();
142       PDBRestResponse resultList = pdbRestCleint.executeRequest(pdbRequest);
143       if (resultList.getSearchSummary() != null
144               && !resultList.getSearchSummary().isEmpty())
145       {
146         discoveredStructuresSet.addAll(resultList.getSearchSummary());
147         updateSequenceDbRef(seq, resultList.getSearchSummary());
148       }
149     }
150
151     int noOfStructuresFound = 0;
152     if (discoveredStructuresSet != null
153             && !discoveredStructuresSet.isEmpty())
154     {
155       jList_FoundStructures.setModel(PDBRestResponse
156               .getListModel(discoveredStructuresSet));
157       tbl_summary.setModel(PDBRestResponse.getTableModel(pdbRequest,
158               discoveredStructuresSet));
159       // resizeColumnWidth(summaryTable);
160       structuresDiscovered = true;
161       noOfStructuresFound = discoveredStructuresSet.size();
162     }
163     String totalTime = (System.currentTimeMillis() - startTime)
164             + " milli secs";
165     mainFrame.setTitle("Structure Chooser - " + noOfStructuresFound
166             + " Found (" + totalTime + ")");
167   }
168
169   /**
170    * Update the DBRef entry for a given sequence with values retrieved from
171    * PDBResponseSummary
172    * 
173    * @param seq
174    *          the Sequence to update its DBRef entry
175    * @param responseSummaries
176    *          a collection of PDBResponseSummary
177    */
178   public void updateSequenceDbRef(SequenceI seq,
179           Collection<PDBResponseSummary> responseSummaries)
180   {
181     for (PDBResponseSummary response : responseSummaries)
182     {
183       PDBEntry newEntry = new PDBEntry();
184       newEntry.setId(response.getPdbId());
185       newEntry.setType("PDB");
186       seq.getDatasetSequence().addPDBId(newEntry);
187     }
188   }
189
190   /**
191    * Builds a query string for a given sequences using its DBRef entries
192    * 
193    * @param seq
194    *          the sequences to build a query for
195    * @return the built query string
196    */
197   @SuppressWarnings("unchecked")
198   public static String buildQuery(SequenceI seq)
199   {
200     String query = seq.getName();
201     StringBuilder queryBuilder = new StringBuilder();
202     int count = 0;
203
204     if (seq.getPDBId() != null)
205     {
206       for (PDBEntry entry : (Vector<PDBEntry>) seq.getPDBId())
207       {
208         queryBuilder.append("text:").append(entry.getId()).append(" OR ");
209       }
210     }
211
212     if (seq.getDBRef() != null && seq.getDBRef().length != 0)
213     {
214       for (DBRefEntry dbRef : seq.getDBRef())
215       {
216         queryBuilder.append("text:")
217                 .append(dbRef.getAccessionId().replaceAll("GO:", ""))
218                 .append(" OR ");
219         ++count;
220         if (count > 10)
221         {
222           break;
223         }
224       }
225       int endIndex = queryBuilder.lastIndexOf(" OR ");
226       query = queryBuilder.toString().substring(5, endIndex);
227     }
228     // System.out.println("Query -----> " + query);
229     return query;
230   }
231
232   /**
233    * Filters a given list of discovered structures based on supplied argument
234    * 
235    * @param fieldToFilterBy
236    *          the field to filter by
237    */
238   public void filterResultSet(final String fieldToFilterBy)
239   {
240     Thread filterThread = new Thread(new Runnable()
241     {
242       @Override
243       public void run()
244       {
245         try
246         {
247           lbl_loading.setVisible(true);
248           pdbRequest.setResponseSize(1);
249           pdbRequest.setFieldToSearchBy("(text:");
250           pdbRequest.setFieldToSortBy(fieldToFilterBy,
251                   !chk_invertFilter.isSelected());
252
253           Collection<PDBResponseSummary> filteredResponse = new HashSet<PDBResponseSummary>();
254           for (SequenceI seq : selectedSequences)
255           {
256             pdbRequest.setSearchTerm(buildQuery(seq) + ")");
257             pdbRequest.setAssociatedSequence(seq.getName());
258             pdbRestCleint = new PDBRestClient();
259             PDBRestResponse resultList = pdbRestCleint
260                     .executeRequest(pdbRequest);
261             if (resultList.getSearchSummary() != null
262                     && !resultList.getSearchSummary().isEmpty())
263             {
264               filteredResponse.addAll(resultList.getSearchSummary());
265             }
266           }
267
268           if (filteredResponse != null)
269           {
270             int filterResponseCount = filteredResponse.size();
271             List<PDBResponseSummary> originalDiscoveredStructuresList = new ArrayList<PDBResponseSummary>(
272                     discoveredStructuresSet);
273             originalDiscoveredStructuresList.removeAll(filteredResponse);
274
275             Collection<PDBResponseSummary> reorderedStructuresSet = new ArrayList<PDBResponseSummary>();
276             reorderedStructuresSet.addAll(filteredResponse);
277             reorderedStructuresSet.addAll(originalDiscoveredStructuresList);
278
279             jList_FoundStructures.setModel(PDBRestResponse
280                     .getListModel(reorderedStructuresSet));
281             tbl_summary.setModel(PDBRestResponse.getTableModel(pdbRequest,
282                     reorderedStructuresSet));
283
284             // int[] filterIndice = new int[filterResponseCount];
285             ListSelectionModel model = tbl_summary.getSelectionModel();
286             model.clearSelection();
287             for (int x = 0; x < filterResponseCount; x++)
288             {
289               // filterIndice[x] = x;
290               model.addSelectionInterval(x, x);
291             }
292
293             // Discard unwanted objects to make them eligible for garbage
294             // collection
295             originalDiscoveredStructuresList = null;
296             reorderedStructuresSet = null;
297
298             // jListFoundStructures.setSelectedIndices(filterIndice);
299           }
300
301           lbl_loading.setVisible(false);
302         } catch (Exception e)
303         {
304           e.printStackTrace();
305         }
306       }
307     });
308     filterThread.start();
309   }
310
311   /**
312    * Determines the column index for the pdb id in the summary table. The pdb id
313    * serves as a unique identifier for a given row in the summary table
314    * 
315    * @param wantedFeilds
316    *          the available table columns in no particular order
317    * @return the pdb id field column index
318    */
319   public static int getPDBIdColumIndex(Collection<PDBDocField> wantedFeilds)
320   {
321     int pdbFeildIndex = 1;
322     for (PDBDocField feild : wantedFeilds)
323     {
324       if (feild.equals(PDBDocField.PDB_ID))
325       {
326         break;
327       }
328       ++pdbFeildIndex;
329     }
330     return pdbFeildIndex;
331   }
332
333   /**
334    * Handles action event for btn_pdbFromFile
335    */
336   public void pdbFromFile_actionPerformed()
337   {
338     jalview.io.JalviewFileChooser chooser = new jalview.io.JalviewFileChooser(
339             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
340     chooser.setFileView(new jalview.io.JalviewFileView());
341     chooser.setDialogTitle(MessageManager.formatMessage(
342             "label.select_pdb_file_for", new String[]
343             { selectedSequence.getDisplayId(false) }));
344     chooser.setToolTipText(MessageManager.formatMessage(
345             "label.load_pdb_file_associate_with_sequence", new String[]
346             { selectedSequence.getDisplayId(false) }));
347
348     int value = chooser.showOpenDialog(null);
349     if (value == jalview.io.JalviewFileChooser.APPROVE_OPTION)
350     {
351       String choice = chooser.getSelectedFile().getPath();
352       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
353       new AssociatePdbFileWithSeq().associatePdbWithSeq(choice,
354               jalview.io.AppletFormatAdapter.FILE, selectedSequence, true,
355               Desktop.instance);
356     }
357   }
358
359   /**
360    * Handles action event for btn_ok
361    */
362   @Override
363   public void ok_ActionPerformed()
364   {
365     int pdbIdCol = getPDBIdColumIndex(pdbRequest.getWantedFields());
366     int[] selectedRows = tbl_summary.getSelectedRows();
367     PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
368     int count = 0;
369     for (int summaryRow : selectedRows)
370     {
371       String pdbIdStr = tbl_summary.getValueAt(summaryRow, pdbIdCol)
372               .toString();
373       PDBEntry pdbEntry = new PDBEntry();
374       pdbEntry.setId(pdbIdStr);
375       pdbEntry.setType("PDB");
376       pdbEntriesToView[count++] = pdbEntry;
377     }
378     new StructureViewer(ap.getStructureSelectionManager()).viewStructures(
379             ap, pdbEntriesToView, ap.av.collateForPDB(pdbEntriesToView));
380   }
381
382   /**
383    * Handles action event for manual entry of pdb ids
384    */
385   public void enterPDB_actionPerformed()
386   {
387     String id = JOptionPane.showInternalInputDialog(Desktop.desktop,
388             MessageManager.getString("label.enter_pdb_id"),
389             MessageManager.getString("label.enter_pdb_id"),
390             JOptionPane.QUESTION_MESSAGE);
391     if (id != null && id.length() > 0)
392     {
393       PDBEntry entry = new PDBEntry();
394       entry.setId(id.toUpperCase());
395       selectedSequence.getDatasetSequence().addPDBId(entry);
396     }
397   }
398
399   /**
400    * Populates the filter combo-box options dynamically depending on discovered
401    * structures
402    */
403   protected void populateFilterComboBox()
404   {
405     if (isStructuresDiscovered())
406     {
407       cmb_filterOption.addItem(new FilterOption("Best Quality",
408               PDBDocField.OVERALL_QUALITY.getCode(), VIEWS_FILTER));
409       cmb_filterOption.addItem(new FilterOption("Best UniProt Coverage",
410               PDBDocField.UNIPROT_COVERAGE.getCode(), VIEWS_FILTER));
411       cmb_filterOption.addItem(new FilterOption("Highest Resolution",
412               PDBDocField.RESOLUTION.getCode(), VIEWS_FILTER));
413       cmb_filterOption.addItem(new FilterOption("Highest Protein Chain",
414               PDBDocField.PROTEIN_CHAIN_COUNT.getCode(), VIEWS_FILTER));
415       cmb_filterOption.addItem(new FilterOption("Highest Bound Molecules",
416               PDBDocField.BOUND_MOLECULE_COUNT.getCode(), VIEWS_FILTER));
417       cmb_filterOption.addItem(new FilterOption("Highest Polymer Residues",
418               PDBDocField.POLYMER_RESIDUE_COUNT.getCode(), VIEWS_FILTER));
419     }
420     cmb_filterOption.addItem(new FilterOption("Enter PDB Id", "-",
421             VIEWS_ENTER_ID));
422     cmb_filterOption.addItem(new FilterOption("From File", "-",
423             VIEWS_FROM_FILE));
424   }
425
426   /**
427    * Updates the displayed view based on the selected filter option
428    */
429   protected void updateCurrentView()
430   {
431     FilterOption selectedOption = ((FilterOption) cmb_filterOption
432             .getSelectedItem());
433     layout_switchableViews.show(pnl_switchableViews,
434             selectedOption.getView());
435     chk_invertFilter.setEnabled(false);
436     if (selectedOption.getView() == VIEWS_FILTER)
437     {
438       chk_invertFilter.setEnabled(true);
439       filterResultSet(selectedOption.getValue());
440     }
441   }
442
443   /**
444    * Handles the state change event for the 'filter' combo-box and 'invert'
445    * check-box
446    */
447   @Override
448   protected void stateChanged(ItemEvent e)
449   {
450     if (e.getSource() instanceof JCheckBox)
451     {
452       updateCurrentView();
453     }
454     else
455     {
456       if (e.getStateChange() == ItemEvent.SELECTED)
457       {
458         updateCurrentView();
459       }
460     }
461
462   }
463
464
465   public boolean isStructuresDiscovered()
466   {
467     return structuresDiscovered;
468   }
469
470   public void setStructuresDiscovered(boolean structuresDiscovered)
471   {
472     this.structuresDiscovered = structuresDiscovered;
473   }
474
475   public Collection<PDBResponseSummary> getDiscoveredStructuresSet()
476   {
477     return discoveredStructuresSet;
478   }
479 }