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