JAL-2026 Bug fix for best quality filter
[jalview.git] / src / jalview / gui / StructureChooser.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
22 package jalview.gui;
23
24 import jalview.bin.Jalview;
25 import jalview.datamodel.DBRefEntry;
26 import jalview.datamodel.DBRefSource;
27 import jalview.datamodel.PDBEntry;
28 import jalview.datamodel.SequenceI;
29 import jalview.jbgui.GStructureChooser;
30 import jalview.jbgui.PDBDocFieldPreferences;
31 import jalview.structure.StructureSelectionManager;
32 import jalview.util.MessageManager;
33 import jalview.ws.dbsources.PDBRestClient;
34 import jalview.ws.dbsources.PDBRestClient.PDBDocField;
35 import jalview.ws.sifts.SiftsSettings;
36 import jalview.ws.uimodel.PDBRestRequest;
37 import jalview.ws.uimodel.PDBRestResponse;
38 import jalview.ws.uimodel.PDBRestResponse.PDBResponseSummary;
39
40 import java.awt.event.ItemEvent;
41 import java.util.ArrayList;
42 import java.util.Collection;
43 import java.util.HashSet;
44 import java.util.LinkedHashSet;
45 import java.util.List;
46 import java.util.Objects;
47
48 import javax.swing.JCheckBox;
49 import javax.swing.JComboBox;
50 import javax.swing.JLabel;
51 import javax.swing.JOptionPane;
52 import javax.swing.table.AbstractTableModel;
53
54 /**
55  * Provides the behaviors for the Structure chooser Panel
56  * 
57  * @author tcnofoegbu
58  *
59  */
60 @SuppressWarnings("serial")
61 public class StructureChooser extends GStructureChooser implements
62         IProgressIndicator
63 {
64   private boolean structuresDiscovered = false;
65
66   private SequenceI selectedSequence;
67
68   private SequenceI[] selectedSequences;
69
70   private IProgressIndicator progressIndicator;
71
72   private Collection<PDBResponseSummary> discoveredStructuresSet;
73
74   private PDBRestRequest lastPdbRequest;
75
76   private PDBRestClient pdbRestCleint;
77
78   private String selectedPdbFileName;
79
80   private boolean isValidPBDEntry;
81
82   public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
83           AlignmentPanel ap)
84   {
85     this.ap = ap;
86     this.selectedSequence = selectedSeq;
87     this.selectedSequences = selectedSeqs;
88     this.progressIndicator = (ap == null) ? null : ap.alignFrame;
89     init();
90   }
91
92   /**
93    * Initializes parameters used by the Structure Chooser Panel
94    */
95   public void init()
96   {
97     if (!Jalview.isHeadlessMode())
98     {
99       progressBar = new ProgressBar(this.statusPanel, this.statusBar);
100     }
101
102     Thread discoverPDBStructuresThread = new Thread(new Runnable()
103     {
104       @Override
105       public void run()
106       {
107         long startTime = System.currentTimeMillis();
108         updateProgressIndicator(MessageManager
109                 .getString("status.loading_cached_pdb_entries"), startTime);
110         loadLocalCachedPDBEntries();
111         updateProgressIndicator(null, startTime);
112         updateProgressIndicator(MessageManager
113                 .getString("status.searching_for_pdb_structures"),
114                 startTime);
115         fetchStructuresMetaData();
116         populateFilterComboBox();
117         updateProgressIndicator(null, startTime);
118         mainFrame.setVisible(true);
119         updateCurrentView();
120       }
121     });
122     discoverPDBStructuresThread.start();
123   }
124
125   /**
126    * Updates the progress indicator with the specified message
127    * 
128    * @param message
129    *          displayed message for the operation
130    * @param id
131    *          unique handle for this indicator
132    */
133   public void updateProgressIndicator(String message, long id)
134   {
135     if (progressIndicator != null)
136     {
137       progressIndicator.setProgressBar(message, id);
138     }
139   }
140
141   /**
142    * Retrieve meta-data for all the structure(s) for a given sequence(s) in a
143    * selection group
144    */
145   public void fetchStructuresMetaData()
146   {
147     long startTime = System.currentTimeMillis();
148     Collection<PDBDocField> wantedFields = PDBDocFieldPreferences
149             .getStructureSummaryFields();
150
151     discoveredStructuresSet = new LinkedHashSet<PDBResponseSummary>();
152     HashSet<String> errors = new HashSet<String>();
153     for (SequenceI seq : selectedSequences)
154     {
155       PDBRestRequest pdbRequest = new PDBRestRequest();
156       pdbRequest.setAllowEmptySeq(false);
157       pdbRequest.setResponseSize(500);
158       pdbRequest.setFieldToSearchBy("(");
159       pdbRequest.setWantedFields(wantedFields);
160       pdbRequest.setSearchTerm(buildQuery(seq) + ")");
161       pdbRequest.setAssociatedSequence(seq);
162       pdbRestCleint = new PDBRestClient();
163       PDBRestResponse resultList;
164       try
165       {
166         resultList = pdbRestCleint.executeRequest(pdbRequest);
167       } catch (Exception e)
168       {
169         e.printStackTrace();
170         errors.add(e.getMessage());
171         continue;
172       }
173       lastPdbRequest = pdbRequest;
174       if (resultList.getSearchSummary() != null
175               && !resultList.getSearchSummary().isEmpty())
176       {
177         discoveredStructuresSet.addAll(resultList.getSearchSummary());
178       }
179     }
180
181     int noOfStructuresFound = 0;
182     String totalTime = (System.currentTimeMillis() - startTime)
183             + " milli secs";
184     if (discoveredStructuresSet != null
185             && !discoveredStructuresSet.isEmpty())
186     {
187       tbl_summary.setModel(PDBRestResponse.getTableModel(lastPdbRequest,
188               discoveredStructuresSet));
189       structuresDiscovered = true;
190       noOfStructuresFound = discoveredStructuresSet.size();
191       mainFrame.setTitle(MessageManager.formatMessage(
192               "label.structure_chooser_no_of_structures",
193               noOfStructuresFound, totalTime));
194     }
195     else
196     {
197       mainFrame.setTitle(MessageManager
198               .getString("label.structure_chooser_manual_association"));
199       if (errors.size() > 0)
200       {
201         StringBuilder errorMsg = new StringBuilder();
202         for (String error : errors)
203         {
204           errorMsg.append(error).append("\n");
205         }
206         JOptionPane.showMessageDialog(this, errorMsg.toString(),
207                 MessageManager.getString("label.pdb_web-service_error"),
208                 JOptionPane.ERROR_MESSAGE);
209       }
210     }
211   }
212
213   public void loadLocalCachedPDBEntries()
214   {
215     ArrayList<CachedPDB> entries = new ArrayList<CachedPDB>();
216     for (SequenceI seq : selectedSequences)
217     {
218       if (seq.getDatasetSequence() != null
219               && seq.getDatasetSequence().getAllPDBEntries() != null)
220       {
221         for (PDBEntry pdbEntry : seq.getDatasetSequence()
222                 .getAllPDBEntries())
223         {
224           if (pdbEntry.getFile() != null)
225           {
226             entries.add(new CachedPDB(seq, pdbEntry));
227           }
228         }
229       }
230     }
231
232     PDBEntryTableModel tableModelx = new PDBEntryTableModel(entries);
233     tbl_local_pdb.setModel(tableModelx);
234   }
235
236   /**
237    * Builds a query string for a given sequences using its DBRef entries
238    * 
239    * @param seq
240    *          the sequences to build a query for
241    * @return the built query string
242    */
243
244   public static String buildQuery(SequenceI seq)
245   {
246     boolean isPDBRefsFound = false;
247     boolean isUniProtRefsFound = false;
248     StringBuilder queryBuilder = new StringBuilder();
249     HashSet<String> seqRefs = new LinkedHashSet<String>();
250
251     if (seq.getAllPDBEntries() != null)
252     {
253       for (PDBEntry entry : seq.getAllPDBEntries())
254       {
255         if (isValidSeqName(entry.getId()))
256         {
257           queryBuilder.append(PDBRestClient.PDBDocField.PDB_ID.getCode())
258                   .append(":")
259 .append(entry.getId().toLowerCase())
260                   .append(" OR ");
261           isPDBRefsFound = true;
262           // seqRefs.add(entry.getId());
263         }
264       }
265     }
266
267     if (seq.getDBRefs() != null && seq.getDBRefs().length != 0)
268     {
269       for (DBRefEntry dbRef : seq.getDBRefs())
270       {
271         if (isValidSeqName(getDBRefId(dbRef)))
272         {
273           if (dbRef.getSource().equalsIgnoreCase(DBRefSource.UNIPROT))
274           {
275             queryBuilder
276                     .append(PDBRestClient.PDBDocField.UNIPROT_ACCESSION
277                             .getCode()).append(":")
278                     .append(getDBRefId(dbRef))
279                     .append(" OR ");
280             queryBuilder
281                     .append(PDBRestClient.PDBDocField.UNIPROT_ID.getCode())
282                     .append(":")
283                     .append(getDBRefId(dbRef)).append(" OR ");
284             isUniProtRefsFound = true;
285           }
286           else if (dbRef.getSource().equalsIgnoreCase(DBRefSource.PDB))
287           {
288
289             queryBuilder.append(PDBRestClient.PDBDocField.PDB_ID.getCode())
290                     .append(":").append(getDBRefId(dbRef).toLowerCase())
291                     .append(" OR ");
292             isPDBRefsFound = true;
293           }
294           else
295           {
296             seqRefs.add(getDBRefId(dbRef));
297           }
298         }
299       }
300     }
301
302     if (!isPDBRefsFound && !isUniProtRefsFound)
303     {
304       String seqName = seq.getName();
305       seqName = sanitizeSeqName(seqName);
306       String[] names = seqName.toLowerCase().split("\\|");
307       for (String name : names)
308       {
309         // System.out.println("Found name : " + name);
310         name.trim();
311         if (isValidSeqName(name))
312         {
313           seqRefs.add(name);
314         }
315       }
316
317       for (String seqRef : seqRefs)
318       {
319         queryBuilder.append("text:").append(seqRef).append(" OR ");
320       }
321     }
322
323     int endIndex = queryBuilder.lastIndexOf(" OR ");
324     if (queryBuilder.toString().length() < 6)
325     {
326       return null;
327     }
328     String query = queryBuilder.toString().substring(0, endIndex);
329     return query;
330   }
331
332   /**
333    * Remove the following special characters from input string +, -, &, |, !, (,
334    * ), {, }, [, ], ^, ", ~, *, ?, :, \
335    * 
336    * @param seqName
337    * @return
338    */
339   private static String sanitizeSeqName(String seqName)
340   {
341     Objects.requireNonNull(seqName);
342     return seqName.replaceAll("\\[\\d*\\]", "")
343             .replaceAll("[^\\dA-Za-z ]", "").replaceAll("\\s+", "+");
344   }
345
346
347   /**
348    * Ensures sequence ref names are not less than 3 characters and does not
349    * contain a database name
350    * 
351    * @param seqName
352    * @return
353    */
354   public static boolean isValidSeqName(String seqName)
355   {
356     // System.out.println("seqName : " + seqName);
357     String ignoreList = "pdb,uniprot,swiss-prot";
358     if (seqName.length() < 3)
359     {
360       return false;
361     }
362     if (seqName.contains(":"))
363     {
364       return false;
365     }
366     seqName = seqName.toLowerCase();
367     for (String ignoredEntry : ignoreList.split(","))
368     {
369       if (seqName.contains(ignoredEntry))
370       {
371         return false;
372       }
373     }
374     return true;
375   }
376
377   public static String getDBRefId(DBRefEntry dbRef)
378   {
379     String ref = dbRef.getAccessionId().replaceAll("GO:", "");
380     return ref;
381   }
382
383   /**
384    * Filters a given list of discovered structures based on supplied argument
385    * 
386    * @param fieldToFilterBy
387    *          the field to filter by
388    */
389   public void filterResultSet(final String fieldToFilterBy)
390   {
391     Thread filterThread = new Thread(new Runnable()
392     {
393       @Override
394       public void run()
395       {
396         long startTime = System.currentTimeMillis();
397         lbl_loading.setVisible(true);
398         Collection<PDBDocField> wantedFields = PDBDocFieldPreferences
399                 .getStructureSummaryFields();
400         Collection<PDBResponseSummary> filteredResponse = new HashSet<PDBResponseSummary>();
401         HashSet<String> errors = new HashSet<String>();
402
403         for (SequenceI seq : selectedSequences)
404         {
405           PDBRestRequest pdbRequest = new PDBRestRequest();
406           if (fieldToFilterBy.equalsIgnoreCase("uniprot_coverage"))
407           {
408             System.out.println(">>>>>> Filtering with uniprot coverate");
409             pdbRequest.setAllowEmptySeq(false);
410             pdbRequest.setResponseSize(1);
411             pdbRequest.setFieldToSearchBy("(");
412             pdbRequest.setSearchTerm(buildQuery(seq) + ")");
413             pdbRequest.setWantedFields(wantedFields);
414             pdbRequest.setAssociatedSequence(seq);
415             pdbRequest.setFacet(true);
416             pdbRequest.setFacetPivot(fieldToFilterBy + ",entry_entity");
417             pdbRequest.setFacetPivotMinCount(1);
418           }
419           else
420           {
421             pdbRequest.setAllowEmptySeq(false);
422             pdbRequest.setResponseSize(1);
423             pdbRequest.setFieldToSearchBy("(");
424             pdbRequest.setFieldToSortBy(fieldToFilterBy,
425                     !chk_invertFilter.isSelected());
426             pdbRequest.setSearchTerm(buildQuery(seq) + ")");
427             pdbRequest.setWantedFields(wantedFields);
428             pdbRequest.setAssociatedSequence(seq);
429           }
430           pdbRestCleint = new PDBRestClient();
431           PDBRestResponse resultList;
432           try
433           {
434             resultList = pdbRestCleint.executeRequest(pdbRequest);
435           } catch (Exception e)
436           {
437             e.printStackTrace();
438             errors.add(e.getMessage());
439             continue;
440           }
441           lastPdbRequest = pdbRequest;
442           if (resultList.getSearchSummary() != null
443                   && !resultList.getSearchSummary().isEmpty())
444           {
445             filteredResponse.addAll(resultList.getSearchSummary());
446           }
447         }
448
449         String totalTime = (System.currentTimeMillis() - startTime)
450                 + " milli secs";
451         if (!filteredResponse.isEmpty())
452         {
453           final int filterResponseCount = filteredResponse.size();
454           Collection<PDBResponseSummary> reorderedStructuresSet = new LinkedHashSet<PDBResponseSummary>();
455           reorderedStructuresSet.addAll(filteredResponse);
456           reorderedStructuresSet.addAll(discoveredStructuresSet);
457           tbl_summary.setModel(PDBRestResponse.getTableModel(
458                   lastPdbRequest, reorderedStructuresSet));
459
460           // Update table selection model here
461           tbl_summary.addRowSelectionInterval(0, filterResponseCount - 1);
462           mainFrame.setTitle(MessageManager.formatMessage(
463                   "label.structure_chooser_filter_time", totalTime));
464         }
465         else
466         {
467           mainFrame.setTitle(MessageManager.formatMessage(
468                   "label.structure_chooser_filter_time", totalTime));
469           if (errors.size() > 0)
470           {
471             StringBuilder errorMsg = new StringBuilder();
472             for (String error : errors)
473             {
474               errorMsg.append(error).append("\n");
475             }
476             JOptionPane.showMessageDialog(
477                     null,
478                     errorMsg.toString(),
479                     MessageManager.getString("label.pdb_web-service_error"),
480                     JOptionPane.ERROR_MESSAGE);
481           }
482         }
483
484         lbl_loading.setVisible(false);
485
486         validateSelections();
487       }
488     });
489     filterThread.start();
490   }
491
492   /**
493    * Handles action event for btn_pdbFromFile
494    */
495   @Override
496   public void pdbFromFile_actionPerformed()
497   {
498     jalview.io.JalviewFileChooser chooser = new jalview.io.JalviewFileChooser(
499             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
500     chooser.setFileView(new jalview.io.JalviewFileView());
501     chooser.setDialogTitle(MessageManager.formatMessage(
502             "label.select_pdb_file_for",
503             selectedSequence.getDisplayId(false)));
504     chooser.setToolTipText(MessageManager.formatMessage(
505             "label.load_pdb_file_associate_with_sequence",
506             selectedSequence.getDisplayId(false)));
507
508     int value = chooser.showOpenDialog(null);
509     if (value == jalview.io.JalviewFileChooser.APPROVE_OPTION)
510     {
511       selectedPdbFileName = chooser.getSelectedFile().getPath();
512       jalview.bin.Cache.setProperty("LAST_DIRECTORY", selectedPdbFileName);
513       validateSelections();
514     }
515   }
516
517   /**
518    * Populates the filter combo-box options dynamically depending on discovered
519    * structures
520    */
521   @Override
522   protected void populateFilterComboBox()
523   {
524     if (isStructuresDiscovered())
525     {
526       cmb_filterOption.addItem(new FilterOption("Best Quality",
527               PDBDocField.OVERALL_QUALITY.getCode(), VIEWS_FILTER));
528       cmb_filterOption.addItem(new FilterOption("Most UniProt Coverage",
529               PDBDocField.UNIPROT_COVERAGE.getCode(), VIEWS_FILTER));
530       cmb_filterOption.addItem(new FilterOption("Best Resolution",
531               PDBDocField.RESOLUTION.getCode(), VIEWS_FILTER));
532       cmb_filterOption.addItem(new FilterOption("Most Protein Chain",
533               PDBDocField.PROTEIN_CHAIN_COUNT.getCode(), VIEWS_FILTER));
534       cmb_filterOption.addItem(new FilterOption("Most Bound Molecules",
535               PDBDocField.BOUND_MOLECULE_COUNT.getCode(), VIEWS_FILTER));
536       cmb_filterOption.addItem(new FilterOption("Most Polymer Residues",
537               PDBDocField.POLYMER_RESIDUE_COUNT.getCode(), VIEWS_FILTER));
538     }
539     cmb_filterOption.addItem(new FilterOption("Enter PDB Id", "-",
540             VIEWS_ENTER_ID));
541     cmb_filterOption.addItem(new FilterOption("From File", "-",
542             VIEWS_FROM_FILE));
543     cmb_filterOption.addItem(new FilterOption("Cached PDB Entries", "-",
544             VIEWS_LOCAL_PDB));
545   }
546
547   /**
548    * Updates the displayed view based on the selected filter option
549    */
550   @Override
551   protected void updateCurrentView()
552   {
553     FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
554             .getSelectedItem());
555     layout_switchableViews.show(pnl_switchableViews,
556             selectedFilterOpt.getView());
557     String filterTitle = mainFrame.getTitle();
558     mainFrame.setTitle(frameTitle);
559     chk_invertFilter.setVisible(false);
560     if (selectedFilterOpt.getView() == VIEWS_FILTER)
561     {
562       mainFrame.setTitle(filterTitle);
563       chk_invertFilter.setVisible(true);
564       filterResultSet(selectedFilterOpt.getValue());
565     }
566     else if (selectedFilterOpt.getView() == VIEWS_ENTER_ID
567             || selectedFilterOpt.getView() == VIEWS_FROM_FILE)
568     {
569       mainFrame.setTitle(MessageManager
570               .getString("label.structure_chooser_manual_association"));
571       idInputAssSeqPanel.loadCmbAssSeq();
572       fileChooserAssSeqPanel.loadCmbAssSeq();
573     }
574     validateSelections();
575   }
576
577   /**
578    * Validates user selection and activates the view button if all parameters
579    * are correct
580    */
581   @Override
582   public void validateSelections()
583   {
584     FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
585             .getSelectedItem());
586     btn_view.setEnabled(false);
587     String currentView = selectedFilterOpt.getView();
588     if (currentView == VIEWS_FILTER)
589     {
590       if (tbl_summary.getSelectedRows().length > 0)
591       {
592         btn_view.setEnabled(true);
593       }
594     }
595     else if (currentView == VIEWS_LOCAL_PDB)
596     {
597       if (tbl_local_pdb.getSelectedRows().length > 0)
598       {
599         btn_view.setEnabled(true);
600       }
601     }
602     else if (currentView == VIEWS_ENTER_ID)
603     {
604       validateAssociationEnterPdb();
605     }
606     else if (currentView == VIEWS_FROM_FILE)
607     {
608       validateAssociationFromFile();
609     }
610   }
611
612   /**
613    * Validates inputs from the Manual PDB entry panel
614    */
615   public void validateAssociationEnterPdb()
616   {
617     AssociateSeqOptions assSeqOpt = (AssociateSeqOptions) idInputAssSeqPanel
618             .getCmb_assSeq().getSelectedItem();
619     lbl_pdbManualFetchStatus.setIcon(errorImage);
620     lbl_pdbManualFetchStatus.setToolTipText("");
621     if (txt_search.getText().length() > 0)
622     {
623       lbl_pdbManualFetchStatus
624               .setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager
625                       .formatMessage("info.no_pdb_entry_found_for",
626                               txt_search.getText())));
627     }
628
629     if (errorWarning.length() > 0)
630     {
631       lbl_pdbManualFetchStatus.setIcon(warningImage);
632       lbl_pdbManualFetchStatus.setToolTipText(JvSwingUtils.wrapTooltip(
633               true, errorWarning.toString()));
634     }
635
636     if (selectedSequences.length == 1
637             || !assSeqOpt.getName().equalsIgnoreCase(
638                     "-Select Associated Seq-"))
639     {
640       txt_search.setEnabled(true);
641       if (isValidPBDEntry)
642       {
643         btn_view.setEnabled(true);
644         lbl_pdbManualFetchStatus.setToolTipText("");
645         lbl_pdbManualFetchStatus.setIcon(goodImage);
646       }
647     }
648     else
649     {
650       txt_search.setEnabled(false);
651       lbl_pdbManualFetchStatus.setIcon(errorImage);
652     }
653   }
654
655   /**
656    * Validates inputs for the manual PDB file selection options
657    */
658   public void validateAssociationFromFile()
659   {
660     AssociateSeqOptions assSeqOpt = (AssociateSeqOptions) fileChooserAssSeqPanel
661             .getCmb_assSeq().getSelectedItem();
662     lbl_fromFileStatus.setIcon(errorImage);
663     if (selectedSequences.length == 1
664             || (assSeqOpt != null && !assSeqOpt.getName().equalsIgnoreCase(
665                     "-Select Associated Seq-")))
666     {
667       btn_pdbFromFile.setEnabled(true);
668       if (selectedPdbFileName != null && selectedPdbFileName.length() > 0)
669       {
670         btn_view.setEnabled(true);
671         lbl_fromFileStatus.setIcon(goodImage);
672       }
673     }
674     else
675     {
676       btn_pdbFromFile.setEnabled(false);
677       lbl_fromFileStatus.setIcon(errorImage);
678     }
679   }
680
681   @Override
682   public void cmbAssSeqStateChanged()
683   {
684     validateSelections();
685   }
686
687   /**
688    * Handles the state change event for the 'filter' combo-box and 'invert'
689    * check-box
690    */
691   @Override
692   protected void stateChanged(ItemEvent e)
693   {
694     if (e.getSource() instanceof JCheckBox)
695     {
696       updateCurrentView();
697     }
698     else
699     {
700       if (e.getStateChange() == ItemEvent.SELECTED)
701       {
702         updateCurrentView();
703       }
704     }
705
706   }
707
708   /**
709    * Handles action event for btn_ok
710    */
711   @Override
712   public void ok_ActionPerformed()
713   {
714     final long progressSessionId = System.currentTimeMillis();
715     final StructureSelectionManager ssm = ap.getStructureSelectionManager();
716     ssm.setProgressIndicator(this);
717     ssm.setProgressSessionId(progressSessionId);
718     new Thread(new Runnable()
719     {
720       @Override
721       public void run()
722       {
723     FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
724             .getSelectedItem());
725     String currentView = selectedFilterOpt.getView();
726     if (currentView == VIEWS_FILTER)
727     {
728       int pdbIdColIndex = tbl_summary.getColumn(
729               PDBRestClient.PDBDocField.PDB_ID.getName()).getModelIndex();
730       int refSeqColIndex = tbl_summary.getColumn("Ref Sequence")
731               .getModelIndex();
732       int[] selectedRows = tbl_summary.getSelectedRows();
733       PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
734       int count = 0;
735       ArrayList<SequenceI> selectedSeqsToView = new ArrayList<SequenceI>();
736       for (int row : selectedRows)
737       {
738         String pdbIdStr = tbl_summary.getValueAt(row, pdbIdColIndex)
739                 .toString();
740         SequenceI selectedSeq = (SequenceI) tbl_summary.getValueAt(row,
741                 refSeqColIndex);
742         selectedSeqsToView.add(selectedSeq);
743         PDBEntry pdbEntry = selectedSeq.getPDBEntry(pdbIdStr);
744         if (pdbEntry == null)
745         {
746           pdbEntry = new PDBEntry();
747           pdbEntry.setId(pdbIdStr);
748           pdbEntry.setType(PDBEntry.Type.PDB);
749           selectedSeq.getDatasetSequence().addPDBId(pdbEntry);
750         }
751         pdbEntriesToView[count++] = pdbEntry;
752       }
753       SequenceI[] selectedSeqs = selectedSeqsToView
754               .toArray(new SequenceI[selectedSeqsToView.size()]);
755           launchStructureViewer(ssm, pdbEntriesToView, ap, selectedSeqs);
756     }
757     else if (currentView == VIEWS_LOCAL_PDB)
758     {
759       int[] selectedRows = tbl_local_pdb.getSelectedRows();
760       PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
761       int count = 0;
762       int pdbIdColIndex = tbl_local_pdb.getColumn(
763               PDBRestClient.PDBDocField.PDB_ID.getName()).getModelIndex();
764       int refSeqColIndex = tbl_local_pdb.getColumn("Ref Sequence")
765               .getModelIndex();
766       ArrayList<SequenceI> selectedSeqsToView = new ArrayList<SequenceI>();
767       for (int row : selectedRows)
768       {
769         PDBEntry pdbEntry = (PDBEntry) tbl_local_pdb.getValueAt(row,
770                 pdbIdColIndex);
771         pdbEntriesToView[count++] = pdbEntry;
772         SequenceI selectedSeq = (SequenceI) tbl_local_pdb.getValueAt(row,
773                 refSeqColIndex);
774         selectedSeqsToView.add(selectedSeq);
775       }
776       SequenceI[] selectedSeqs = selectedSeqsToView
777               .toArray(new SequenceI[selectedSeqsToView.size()]);
778           launchStructureViewer(ssm, pdbEntriesToView, ap, selectedSeqs);
779     }
780     else if (currentView == VIEWS_ENTER_ID)
781     {
782       SequenceI userSelectedSeq = ((AssociateSeqOptions) idInputAssSeqPanel
783               .getCmb_assSeq().getSelectedItem()).getSequence();
784       if (userSelectedSeq != null)
785       {
786         selectedSequence = userSelectedSeq;
787       }
788
789       String pdbIdStr = txt_search.getText();
790       PDBEntry pdbEntry = selectedSequence.getPDBEntry(pdbIdStr);
791       if (pdbEntry == null)
792       {
793         pdbEntry = new PDBEntry();
794         pdbEntry.setId(pdbIdStr);
795         pdbEntry.setType(PDBEntry.Type.PDB);
796         selectedSequence.getDatasetSequence().addPDBId(pdbEntry);
797       }
798
799       PDBEntry[] pdbEntriesToView = new PDBEntry[] { pdbEntry };
800           launchStructureViewer(ssm, pdbEntriesToView, ap,
801                   new SequenceI[] { selectedSequence });
802     }
803     else if (currentView == VIEWS_FROM_FILE)
804     {
805       SequenceI userSelectedSeq = ((AssociateSeqOptions) fileChooserAssSeqPanel
806               .getCmb_assSeq().getSelectedItem()).getSequence();
807       if (userSelectedSeq != null)
808       {
809         selectedSequence = userSelectedSeq;
810       }
811       PDBEntry fileEntry = new AssociatePdbFileWithSeq()
812               .associatePdbWithSeq(selectedPdbFileName,
813                       jalview.io.AppletFormatAdapter.FILE,
814                       selectedSequence, true, Desktop.instance);
815
816           launchStructureViewer(ssm, new PDBEntry[] { fileEntry }, ap,
817                   new SequenceI[] { selectedSequence });
818     }
819     mainFrame.dispose();
820       }
821     }).start();
822   }
823
824   private void launchStructureViewer(StructureSelectionManager ssm,
825           final PDBEntry[] pdbEntriesToView,
826           final AlignmentPanel alignPanel, SequenceI[] sequences)
827   {
828     ssm.setProgressBar("Launching PDB structure viewer..");
829     final StructureViewer sViewer = new StructureViewer(ssm);
830
831     if (SiftsSettings.isMapWithSifts())
832     {
833       for (SequenceI seq : sequences)
834       {
835         if (seq.getSourceDBRef() == null)
836         {
837           ssm.setProgressBar(null);
838           ssm.setProgressBar("Fetching Database refs..");
839           new jalview.ws.DBRefFetcher(sequences, null, null, null, false)
840                   .fetchDBRefs(true);
841           break;
842         }
843       }
844     }
845         if (pdbEntriesToView.length > 1)
846         {
847           ArrayList<SequenceI[]> seqsMap = new ArrayList<SequenceI[]>();
848           for (SequenceI seq : sequences)
849           {
850             seqsMap.add(new SequenceI[] { seq });
851           }
852           SequenceI[][] collatedSeqs = seqsMap.toArray(new SequenceI[0][0]);
853       ssm.setProgressBar(null);
854       ssm.setProgressBar("Fetching PDB Structures for selected entries..");
855           sViewer.viewStructures(pdbEntriesToView, collatedSeqs, alignPanel);
856         }
857         else
858         {
859       ssm.setProgressBar(null);
860       ssm.setProgressBar("Fetching PDB Structure for "
861               + pdbEntriesToView[0].getId());
862           sViewer.viewStructures(pdbEntriesToView[0], sequences, alignPanel);
863         }
864   }
865
866   /**
867    * Populates the combo-box used in associating manually fetched structures to
868    * a unique sequence when more than one sequence selection is made.
869    */
870   @Override
871   public void populateCmbAssociateSeqOptions(
872           JComboBox<AssociateSeqOptions> cmb_assSeq, JLabel lbl_associateSeq)
873   {
874     cmb_assSeq.removeAllItems();
875     cmb_assSeq.addItem(new AssociateSeqOptions("-Select Associated Seq-",
876             null));
877     lbl_associateSeq.setVisible(false);
878     if (selectedSequences.length > 1)
879     {
880       for (SequenceI seq : selectedSequences)
881       {
882         cmb_assSeq.addItem(new AssociateSeqOptions(seq));
883       }
884     }
885     else
886     {
887       String seqName = selectedSequence.getDisplayId(false);
888       seqName = seqName.length() <= 40 ? seqName : seqName.substring(0, 39);
889       lbl_associateSeq.setText(seqName);
890       lbl_associateSeq.setVisible(true);
891       cmb_assSeq.setVisible(false);
892     }
893   }
894
895   public boolean isStructuresDiscovered()
896   {
897     return structuresDiscovered;
898   }
899
900   public void setStructuresDiscovered(boolean structuresDiscovered)
901   {
902     this.structuresDiscovered = structuresDiscovered;
903   }
904
905   public Collection<PDBResponseSummary> getDiscoveredStructuresSet()
906   {
907     return discoveredStructuresSet;
908   }
909
910   @Override
911   protected void txt_search_ActionPerformed()
912   {
913     new Thread()
914     {
915       @Override
916       public void run()
917       {
918         errorWarning.setLength(0);
919         isValidPBDEntry = false;
920         if (txt_search.getText().length() > 0)
921         {
922           List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
923           wantedFields.add(PDBDocField.PDB_ID);
924           PDBRestRequest pdbRequest = new PDBRestRequest();
925           pdbRequest.setAllowEmptySeq(false);
926           pdbRequest.setResponseSize(1);
927           pdbRequest.setFieldToSearchBy("(pdb_id:");
928           pdbRequest.setWantedFields(wantedFields);
929           pdbRequest
930                   .setSearchTerm(txt_search.getText().toLowerCase() + ")");
931           pdbRequest.setAssociatedSequence(selectedSequence);
932           pdbRestCleint = new PDBRestClient();
933           PDBRestResponse resultList;
934           try
935           {
936             resultList = pdbRestCleint.executeRequest(pdbRequest);
937           } catch (Exception e)
938           {
939             errorWarning.append(e.getMessage());
940             return;
941           } finally
942           {
943             validateSelections();
944           }
945           if (resultList.getSearchSummary() != null
946                   && resultList.getSearchSummary().size() > 0)
947           {
948             isValidPBDEntry = true;
949           }
950         }
951         validateSelections();
952       }
953     }.start();
954   }
955
956   @Override
957   public void tabRefresh()
958   {
959     if (selectedSequences != null)
960     {
961       Thread refreshThread = new Thread(new Runnable()
962       {
963         @Override
964         public void run()
965         {
966           fetchStructuresMetaData();
967           filterResultSet(((FilterOption) cmb_filterOption
968                   .getSelectedItem()).getValue());
969         }
970       });
971       refreshThread.start();
972     }
973   }
974
975   public class PDBEntryTableModel extends AbstractTableModel
976   {
977     String[] columns = { "Ref Sequence", "PDB Id", "Chain", "Type", "File" };
978
979     private List<CachedPDB> pdbEntries;
980
981     public PDBEntryTableModel(List<CachedPDB> pdbEntries)
982     {
983       this.pdbEntries = new ArrayList<CachedPDB>(pdbEntries);
984     }
985
986     @Override
987     public String getColumnName(int columnIndex)
988     {
989       return columns[columnIndex];
990     }
991
992     @Override
993     public int getRowCount()
994     {
995       return pdbEntries.size();
996     }
997
998     @Override
999     public int getColumnCount()
1000     {
1001       return columns.length;
1002     }
1003
1004     @Override
1005     public boolean isCellEditable(int row, int column)
1006     {
1007       return false;
1008     }
1009
1010     @Override
1011     public Object getValueAt(int rowIndex, int columnIndex)
1012     {
1013       Object value = "??";
1014       CachedPDB entry = pdbEntries.get(rowIndex);
1015       switch (columnIndex)
1016       {
1017       case 0:
1018         value = entry.getSequence();
1019         break;
1020       case 1:
1021         value = entry.getPdbEntry();
1022         break;
1023       case 2:
1024         value = entry.getPdbEntry().getChainCode() == null ? "_" : entry
1025                 .getPdbEntry().getChainCode();
1026         break;
1027       case 3:
1028         value = entry.getPdbEntry().getType();
1029         break;
1030       case 4:
1031         value = entry.getPdbEntry().getFile();
1032         break;
1033       }
1034       return value;
1035     }
1036
1037     @Override
1038     public Class<?> getColumnClass(int columnIndex)
1039     {
1040       return columnIndex == 0 ? SequenceI.class : PDBEntry.class;
1041     }
1042
1043     public CachedPDB getPDBEntryAt(int row)
1044     {
1045       return pdbEntries.get(row);
1046     }
1047
1048   }
1049
1050   private class CachedPDB
1051   {
1052     private SequenceI sequence;
1053
1054     private PDBEntry pdbEntry;
1055
1056     public CachedPDB(SequenceI sequence, PDBEntry pdbEntry)
1057     {
1058       this.sequence = sequence;
1059       this.pdbEntry = pdbEntry;
1060     }
1061
1062     public SequenceI getSequence()
1063     {
1064       return sequence;
1065     }
1066
1067     public PDBEntry getPdbEntry()
1068     {
1069       return pdbEntry;
1070     }
1071
1072   }
1073
1074   private IProgressIndicator progressBar;
1075
1076   @Override
1077   public void setProgressBar(String message, long id)
1078   {
1079     progressBar.setProgressBar(message, id);
1080   }
1081
1082   @Override
1083   public void registerHandler(long id, IProgressIndicatorHandler handler)
1084   {
1085     progressBar.registerHandler(id, handler);
1086   }
1087
1088   @Override
1089   public boolean operationInProgress()
1090   {
1091     return progressBar.operationInProgress();
1092   }
1093 }