JAL-1720 JAL-2002 improvement for 'uniprot coverage' filter using facet pivot query
[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         for (SequenceI seq : selectedSequences)
386         {
387           PDBRestRequest pdbRequest = new PDBRestRequest();
388           if (fieldToFilterBy.equalsIgnoreCase("uniprot_coverage"))
389           {
390             System.out.println(">>>>>> Filtering with uniprot coverate");
391             pdbRequest.setAllowEmptySeq(false);
392             pdbRequest.setResponseSize(1);
393             pdbRequest.setFieldToSearchBy("(");
394             pdbRequest.setSearchTerm(buildQuery(seq) + ")");
395             pdbRequest.setWantedFields(wantedFields);
396             pdbRequest.setAssociatedSequence(seq);
397             pdbRequest.setFacet(true);
398             pdbRequest.setFacetPivot(fieldToFilterBy + ",entry_entity");
399             pdbRequest.setFacetPivotMinCount(1);
400           }
401           else
402           {
403             pdbRequest.setAllowEmptySeq(false);
404             pdbRequest.setResponseSize(1);
405             pdbRequest.setFieldToSearchBy("(");
406             pdbRequest.setFieldToSortBy(fieldToFilterBy,
407                     !chk_invertFilter.isSelected());
408             pdbRequest.setSearchTerm(buildQuery(seq) + ")");
409             pdbRequest.setWantedFields(wantedFields);
410             pdbRequest.setAssociatedSequence(seq);
411           }
412           pdbRestCleint = new PDBRestClient();
413           PDBRestResponse resultList;
414           try
415           {
416             resultList = pdbRestCleint.executeRequest(pdbRequest);
417           } catch (Exception e)
418           {
419             e.printStackTrace();
420             errors.add(e.getMessage());
421             continue;
422           }
423           lastPdbRequest = pdbRequest;
424           if (resultList.getSearchSummary() != null
425                   && !resultList.getSearchSummary().isEmpty())
426           {
427             filteredResponse.addAll(resultList.getSearchSummary());
428           }
429         }
430
431         String totalTime = (System.currentTimeMillis() - startTime)
432                 + " milli secs";
433         if (!filteredResponse.isEmpty())
434         {
435           final int filterResponseCount = filteredResponse.size();
436           Collection<PDBResponseSummary> reorderedStructuresSet = new LinkedHashSet<PDBResponseSummary>();
437           reorderedStructuresSet.addAll(filteredResponse);
438           reorderedStructuresSet.addAll(discoveredStructuresSet);
439           tbl_summary.setModel(PDBRestResponse.getTableModel(
440                   lastPdbRequest, reorderedStructuresSet));
441
442           // Update table selection model here
443           tbl_summary.addRowSelectionInterval(0, filterResponseCount - 1);
444           mainFrame.setTitle(MessageManager.formatMessage(
445                   "label.structure_chooser_filter_time", totalTime));
446         }
447         else
448         {
449           mainFrame.setTitle(MessageManager.formatMessage(
450                   "label.structure_chooser_filter_time", totalTime));
451           if (errors.size() > 0)
452           {
453             StringBuilder errorMsg = new StringBuilder();
454             for (String error : errors)
455             {
456               errorMsg.append(error).append("\n");
457             }
458             JOptionPane.showMessageDialog(
459                     null,
460                     errorMsg.toString(),
461                     MessageManager.getString("label.pdb_web-service_error"),
462                     JOptionPane.ERROR_MESSAGE);
463           }
464         }
465
466         lbl_loading.setVisible(false);
467
468         validateSelections();
469       }
470     });
471     filterThread.start();
472   }
473
474   /**
475    * Handles action event for btn_pdbFromFile
476    */
477   @Override
478   public void pdbFromFile_actionPerformed()
479   {
480     jalview.io.JalviewFileChooser chooser = new jalview.io.JalviewFileChooser(
481             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
482     chooser.setFileView(new jalview.io.JalviewFileView());
483     chooser.setDialogTitle(MessageManager.formatMessage(
484             "label.select_pdb_file_for",
485             selectedSequence.getDisplayId(false)));
486     chooser.setToolTipText(MessageManager.formatMessage(
487             "label.load_pdb_file_associate_with_sequence",
488             selectedSequence.getDisplayId(false)));
489
490     int value = chooser.showOpenDialog(null);
491     if (value == jalview.io.JalviewFileChooser.APPROVE_OPTION)
492     {
493       selectedPdbFileName = chooser.getSelectedFile().getPath();
494       jalview.bin.Cache.setProperty("LAST_DIRECTORY", selectedPdbFileName);
495       validateSelections();
496     }
497   }
498
499   /**
500    * Populates the filter combo-box options dynamically depending on discovered
501    * structures
502    */
503   @Override
504   protected void populateFilterComboBox()
505   {
506     if (isStructuresDiscovered())
507     {
508       cmb_filterOption.addItem(new FilterOption("Best Quality",
509               PDBDocField.OVERALL_QUALITY.getCode(), VIEWS_FILTER));
510       cmb_filterOption.addItem(new FilterOption("Best UniProt Coverage",
511               PDBDocField.UNIPROT_COVERAGE.getCode(), VIEWS_FILTER));
512       cmb_filterOption.addItem(new FilterOption("Highest Resolution",
513               PDBDocField.RESOLUTION.getCode(), VIEWS_FILTER));
514       cmb_filterOption.addItem(new FilterOption("Highest Protein Chain",
515               PDBDocField.PROTEIN_CHAIN_COUNT.getCode(), VIEWS_FILTER));
516       cmb_filterOption.addItem(new FilterOption("Highest Bound Molecules",
517               PDBDocField.BOUND_MOLECULE_COUNT.getCode(), VIEWS_FILTER));
518       cmb_filterOption.addItem(new FilterOption("Highest Polymer Residues",
519               PDBDocField.POLYMER_RESIDUE_COUNT.getCode(), VIEWS_FILTER));
520     }
521     cmb_filterOption.addItem(new FilterOption("Enter PDB Id", "-",
522             VIEWS_ENTER_ID));
523     cmb_filterOption.addItem(new FilterOption("From File", "-",
524             VIEWS_FROM_FILE));
525     cmb_filterOption.addItem(new FilterOption("Cached PDB Entries", "-",
526             VIEWS_LOCAL_PDB));
527   }
528
529   /**
530    * Updates the displayed view based on the selected filter option
531    */
532   @Override
533   protected void updateCurrentView()
534   {
535     FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
536             .getSelectedItem());
537     layout_switchableViews.show(pnl_switchableViews,
538             selectedFilterOpt.getView());
539     String filterTitle = mainFrame.getTitle();
540     mainFrame.setTitle(frameTitle);
541     chk_invertFilter.setVisible(false);
542     if (selectedFilterOpt.getView() == VIEWS_FILTER)
543     {
544       mainFrame.setTitle(filterTitle);
545       chk_invertFilter.setVisible(true);
546       filterResultSet(selectedFilterOpt.getValue());
547     }
548     else if (selectedFilterOpt.getView() == VIEWS_ENTER_ID
549             || selectedFilterOpt.getView() == VIEWS_FROM_FILE)
550     {
551       mainFrame.setTitle(MessageManager
552               .getString("label.structure_chooser_manual_association"));
553       idInputAssSeqPanel.loadCmbAssSeq();
554       fileChooserAssSeqPanel.loadCmbAssSeq();
555     }
556     validateSelections();
557   }
558
559   /**
560    * Validates user selection and activates the view button if all parameters
561    * are correct
562    */
563   @Override
564   public void validateSelections()
565   {
566     FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
567             .getSelectedItem());
568     btn_view.setEnabled(false);
569     String currentView = selectedFilterOpt.getView();
570     if (currentView == VIEWS_FILTER)
571     {
572       if (tbl_summary.getSelectedRows().length > 0)
573       {
574         btn_view.setEnabled(true);
575       }
576     }
577     else if (currentView == VIEWS_LOCAL_PDB)
578     {
579       if (tbl_local_pdb.getSelectedRows().length > 0)
580       {
581         btn_view.setEnabled(true);
582       }
583     }
584     else if (currentView == VIEWS_ENTER_ID)
585     {
586       validateAssociationEnterPdb();
587     }
588     else if (currentView == VIEWS_FROM_FILE)
589     {
590       validateAssociationFromFile();
591     }
592   }
593
594   /**
595    * Validates inputs from the Manual PDB entry panel
596    */
597   public void validateAssociationEnterPdb()
598   {
599     AssociateSeqOptions assSeqOpt = (AssociateSeqOptions) idInputAssSeqPanel
600             .getCmb_assSeq().getSelectedItem();
601     lbl_pdbManualFetchStatus.setIcon(errorImage);
602     lbl_pdbManualFetchStatus.setToolTipText("");
603     if (txt_search.getText().length() > 0)
604     {
605       lbl_pdbManualFetchStatus
606               .setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager
607                       .formatMessage("info.no_pdb_entry_found_for",
608                               txt_search.getText())));
609     }
610
611     if (errorWarning.length() > 0)
612     {
613       lbl_pdbManualFetchStatus.setIcon(warningImage);
614       lbl_pdbManualFetchStatus.setToolTipText(JvSwingUtils.wrapTooltip(
615               true, errorWarning.toString()));
616     }
617
618     if (selectedSequences.length == 1
619             || !assSeqOpt.getName().equalsIgnoreCase(
620                     "-Select Associated Seq-"))
621     {
622       txt_search.setEnabled(true);
623       if (isValidPBDEntry)
624       {
625         btn_view.setEnabled(true);
626         lbl_pdbManualFetchStatus.setToolTipText("");
627         lbl_pdbManualFetchStatus.setIcon(goodImage);
628       }
629     }
630     else
631     {
632       txt_search.setEnabled(false);
633       lbl_pdbManualFetchStatus.setIcon(errorImage);
634     }
635   }
636
637   /**
638    * Validates inputs for the manual PDB file selection options
639    */
640   public void validateAssociationFromFile()
641   {
642     AssociateSeqOptions assSeqOpt = (AssociateSeqOptions) fileChooserAssSeqPanel
643             .getCmb_assSeq().getSelectedItem();
644     lbl_fromFileStatus.setIcon(errorImage);
645     if (selectedSequences.length == 1
646             || (assSeqOpt != null && !assSeqOpt.getName().equalsIgnoreCase(
647                     "-Select Associated Seq-")))
648     {
649       btn_pdbFromFile.setEnabled(true);
650       if (selectedPdbFileName != null && selectedPdbFileName.length() > 0)
651       {
652         btn_view.setEnabled(true);
653         lbl_fromFileStatus.setIcon(goodImage);
654       }
655     }
656     else
657     {
658       btn_pdbFromFile.setEnabled(false);
659       lbl_fromFileStatus.setIcon(errorImage);
660     }
661   }
662
663   @Override
664   public void cmbAssSeqStateChanged()
665   {
666     validateSelections();
667   }
668
669   /**
670    * Handles the state change event for the 'filter' combo-box and 'invert'
671    * check-box
672    */
673   @Override
674   protected void stateChanged(ItemEvent e)
675   {
676     if (e.getSource() instanceof JCheckBox)
677     {
678       updateCurrentView();
679     }
680     else
681     {
682       if (e.getStateChange() == ItemEvent.SELECTED)
683       {
684         updateCurrentView();
685       }
686     }
687
688   }
689
690   /**
691    * Handles action event for btn_ok
692    */
693   @Override
694   public void ok_ActionPerformed()
695   {
696     final long progressSessionId = System.currentTimeMillis();
697     final StructureSelectionManager ssm = ap.getStructureSelectionManager();
698     ssm.setProgressIndicator(this);
699     ssm.setProgressSessionId(progressSessionId);
700     new Thread(new Runnable()
701     {
702       @Override
703       public void run()
704       {
705     FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
706             .getSelectedItem());
707     String currentView = selectedFilterOpt.getView();
708     if (currentView == VIEWS_FILTER)
709     {
710       int pdbIdColIndex = tbl_summary.getColumn(
711               PDBRestClient.PDBDocField.PDB_ID.getName()).getModelIndex();
712       int refSeqColIndex = tbl_summary.getColumn("Ref Sequence")
713               .getModelIndex();
714       int[] selectedRows = tbl_summary.getSelectedRows();
715       PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
716       int count = 0;
717       ArrayList<SequenceI> selectedSeqsToView = new ArrayList<SequenceI>();
718       for (int row : selectedRows)
719       {
720         String pdbIdStr = tbl_summary.getValueAt(row, pdbIdColIndex)
721                 .toString();
722         SequenceI selectedSeq = (SequenceI) tbl_summary.getValueAt(row,
723                 refSeqColIndex);
724         selectedSeqsToView.add(selectedSeq);
725         PDBEntry pdbEntry = selectedSeq.getPDBEntry(pdbIdStr);
726         if (pdbEntry == null)
727         {
728           pdbEntry = new PDBEntry();
729           pdbEntry.setId(pdbIdStr);
730           pdbEntry.setType(PDBEntry.Type.PDB);
731           selectedSeq.getDatasetSequence().addPDBId(pdbEntry);
732         }
733         pdbEntriesToView[count++] = pdbEntry;
734       }
735       SequenceI[] selectedSeqs = selectedSeqsToView
736               .toArray(new SequenceI[selectedSeqsToView.size()]);
737           launchStructureViewer(ssm, pdbEntriesToView, ap, selectedSeqs);
738     }
739     else if (currentView == VIEWS_LOCAL_PDB)
740     {
741       int[] selectedRows = tbl_local_pdb.getSelectedRows();
742       PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
743       int count = 0;
744       int pdbIdColIndex = tbl_local_pdb.getColumn(
745               PDBRestClient.PDBDocField.PDB_ID.getName()).getModelIndex();
746       int refSeqColIndex = tbl_local_pdb.getColumn("Ref Sequence")
747               .getModelIndex();
748       ArrayList<SequenceI> selectedSeqsToView = new ArrayList<SequenceI>();
749       for (int row : selectedRows)
750       {
751         PDBEntry pdbEntry = (PDBEntry) tbl_local_pdb.getValueAt(row,
752                 pdbIdColIndex);
753         pdbEntriesToView[count++] = pdbEntry;
754         SequenceI selectedSeq = (SequenceI) tbl_local_pdb.getValueAt(row,
755                 refSeqColIndex);
756         selectedSeqsToView.add(selectedSeq);
757       }
758       SequenceI[] selectedSeqs = selectedSeqsToView
759               .toArray(new SequenceI[selectedSeqsToView.size()]);
760           launchStructureViewer(ssm, pdbEntriesToView, ap, selectedSeqs);
761     }
762     else if (currentView == VIEWS_ENTER_ID)
763     {
764       SequenceI userSelectedSeq = ((AssociateSeqOptions) idInputAssSeqPanel
765               .getCmb_assSeq().getSelectedItem()).getSequence();
766       if (userSelectedSeq != null)
767       {
768         selectedSequence = userSelectedSeq;
769       }
770
771       String pdbIdStr = txt_search.getText();
772       PDBEntry pdbEntry = selectedSequence.getPDBEntry(pdbIdStr);
773       if (pdbEntry == null)
774       {
775         pdbEntry = new PDBEntry();
776         pdbEntry.setId(pdbIdStr);
777         pdbEntry.setType(PDBEntry.Type.PDB);
778         selectedSequence.getDatasetSequence().addPDBId(pdbEntry);
779       }
780
781       PDBEntry[] pdbEntriesToView = new PDBEntry[] { pdbEntry };
782           launchStructureViewer(ssm, pdbEntriesToView, ap,
783                   new SequenceI[] { selectedSequence });
784     }
785     else if (currentView == VIEWS_FROM_FILE)
786     {
787       SequenceI userSelectedSeq = ((AssociateSeqOptions) fileChooserAssSeqPanel
788               .getCmb_assSeq().getSelectedItem()).getSequence();
789       if (userSelectedSeq != null)
790       {
791         selectedSequence = userSelectedSeq;
792       }
793       PDBEntry fileEntry = new AssociatePdbFileWithSeq()
794               .associatePdbWithSeq(selectedPdbFileName,
795                       jalview.io.AppletFormatAdapter.FILE,
796                       selectedSequence, true, Desktop.instance);
797
798           launchStructureViewer(ssm, new PDBEntry[] { fileEntry }, ap,
799                   new SequenceI[] { selectedSequence });
800     }
801     mainFrame.dispose();
802       }
803     }).start();
804   }
805
806   private void launchStructureViewer(StructureSelectionManager ssm,
807           final PDBEntry[] pdbEntriesToView,
808           final AlignmentPanel alignPanel, SequenceI[] sequences)
809   {
810     ssm.setProgressBar("Launching PDB structure viewer..");
811     final StructureViewer sViewer = new StructureViewer(ssm);
812
813     if (SiftsSettings.isMapWithSifts())
814     {
815       for (SequenceI seq : sequences)
816       {
817         if (seq.getSourceDBRef() == null)
818         {
819           ssm.setProgressBar(null);
820           ssm.setProgressBar("Fetching Database refs..");
821           new jalview.ws.DBRefFetcher(sequences, null, null, null, false)
822                   .fetchDBRefs(true);
823           break;
824         }
825       }
826     }
827         if (pdbEntriesToView.length > 1)
828         {
829           ArrayList<SequenceI[]> seqsMap = new ArrayList<SequenceI[]>();
830           for (SequenceI seq : sequences)
831           {
832             seqsMap.add(new SequenceI[] { seq });
833           }
834           SequenceI[][] collatedSeqs = seqsMap.toArray(new SequenceI[0][0]);
835       ssm.setProgressBar(null);
836       ssm.setProgressBar("Fetching PDB Structures for selected entries..");
837           sViewer.viewStructures(pdbEntriesToView, collatedSeqs, alignPanel);
838         }
839         else
840         {
841       ssm.setProgressBar(null);
842       ssm.setProgressBar("Fetching PDB Structure for "
843               + pdbEntriesToView[0].getId());
844           sViewer.viewStructures(pdbEntriesToView[0], sequences, alignPanel);
845         }
846   }
847
848   /**
849    * Populates the combo-box used in associating manually fetched structures to
850    * a unique sequence when more than one sequence selection is made.
851    */
852   @Override
853   public void populateCmbAssociateSeqOptions(
854           JComboBox<AssociateSeqOptions> cmb_assSeq, JLabel lbl_associateSeq)
855   {
856     cmb_assSeq.removeAllItems();
857     cmb_assSeq.addItem(new AssociateSeqOptions("-Select Associated Seq-",
858             null));
859     lbl_associateSeq.setVisible(false);
860     if (selectedSequences.length > 1)
861     {
862       for (SequenceI seq : selectedSequences)
863       {
864         cmb_assSeq.addItem(new AssociateSeqOptions(seq));
865       }
866     }
867     else
868     {
869       String seqName = selectedSequence.getDisplayId(false);
870       seqName = seqName.length() <= 40 ? seqName : seqName.substring(0, 39);
871       lbl_associateSeq.setText(seqName);
872       lbl_associateSeq.setVisible(true);
873       cmb_assSeq.setVisible(false);
874     }
875   }
876
877   public boolean isStructuresDiscovered()
878   {
879     return structuresDiscovered;
880   }
881
882   public void setStructuresDiscovered(boolean structuresDiscovered)
883   {
884     this.structuresDiscovered = structuresDiscovered;
885   }
886
887   public Collection<PDBResponseSummary> getDiscoveredStructuresSet()
888   {
889     return discoveredStructuresSet;
890   }
891
892   @Override
893   protected void txt_search_ActionPerformed()
894   {
895     new Thread()
896     {
897       @Override
898       public void run()
899       {
900         errorWarning.setLength(0);
901         isValidPBDEntry = false;
902         if (txt_search.getText().length() > 0)
903         {
904           List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
905           wantedFields.add(PDBDocField.PDB_ID);
906           PDBRestRequest pdbRequest = new PDBRestRequest();
907           pdbRequest.setAllowEmptySeq(false);
908           pdbRequest.setResponseSize(1);
909           pdbRequest.setFieldToSearchBy("(pdb_id:");
910           pdbRequest.setWantedFields(wantedFields);
911           pdbRequest.setSearchTerm(txt_search.getText() + ")");
912           pdbRequest.setAssociatedSequence(selectedSequence);
913           pdbRestCleint = new PDBRestClient();
914           PDBRestResponse resultList;
915           try
916           {
917             resultList = pdbRestCleint.executeRequest(pdbRequest);
918           } catch (Exception e)
919           {
920             errorWarning.append(e.getMessage());
921             return;
922           } finally
923           {
924             validateSelections();
925           }
926           if (resultList.getSearchSummary() != null
927                   && resultList.getSearchSummary().size() > 0)
928           {
929             isValidPBDEntry = true;
930           }
931         }
932         validateSelections();
933       }
934     }.start();
935   }
936
937   @Override
938   public void tabRefresh()
939   {
940     if (selectedSequences != null)
941     {
942       Thread refreshThread = new Thread(new Runnable()
943       {
944         @Override
945         public void run()
946         {
947           fetchStructuresMetaData();
948           filterResultSet(((FilterOption) cmb_filterOption
949                   .getSelectedItem()).getValue());
950         }
951       });
952       refreshThread.start();
953     }
954   }
955
956   public class PDBEntryTableModel extends AbstractTableModel
957   {
958     String[] columns = { "Ref Sequence", "PDB Id", "Chain", "Type", "File" };
959
960     private List<CachedPDB> pdbEntries;
961
962     public PDBEntryTableModel(List<CachedPDB> pdbEntries)
963     {
964       this.pdbEntries = new ArrayList<CachedPDB>(pdbEntries);
965     }
966
967     @Override
968     public String getColumnName(int columnIndex)
969     {
970       return columns[columnIndex];
971     }
972
973     @Override
974     public int getRowCount()
975     {
976       return pdbEntries.size();
977     }
978
979     @Override
980     public int getColumnCount()
981     {
982       return columns.length;
983     }
984
985     @Override
986     public boolean isCellEditable(int row, int column)
987     {
988       return false;
989     }
990
991     @Override
992     public Object getValueAt(int rowIndex, int columnIndex)
993     {
994       Object value = "??";
995       CachedPDB entry = pdbEntries.get(rowIndex);
996       switch (columnIndex)
997       {
998       case 0:
999         value = entry.getSequence();
1000         break;
1001       case 1:
1002         value = entry.getPdbEntry();
1003         break;
1004       case 2:
1005         value = entry.getPdbEntry().getChainCode() == null ? "_" : entry
1006                 .getPdbEntry().getChainCode();
1007         break;
1008       case 3:
1009         value = entry.getPdbEntry().getType();
1010         break;
1011       case 4:
1012         value = entry.getPdbEntry().getFile();
1013         break;
1014       }
1015       return value;
1016     }
1017
1018     @Override
1019     public Class<?> getColumnClass(int columnIndex)
1020     {
1021       return columnIndex == 0 ? SequenceI.class : PDBEntry.class;
1022     }
1023
1024     public CachedPDB getPDBEntryAt(int row)
1025     {
1026       return pdbEntries.get(row);
1027     }
1028
1029   }
1030
1031   private class CachedPDB
1032   {
1033     private SequenceI sequence;
1034
1035     private PDBEntry pdbEntry;
1036
1037     public CachedPDB(SequenceI sequence, PDBEntry pdbEntry)
1038     {
1039       this.sequence = sequence;
1040       this.pdbEntry = pdbEntry;
1041     }
1042
1043     public SequenceI getSequence()
1044     {
1045       return sequence;
1046     }
1047
1048     public PDBEntry getPdbEntry()
1049     {
1050       return pdbEntry;
1051     }
1052
1053   }
1054
1055   private IProgressIndicator progressBar;
1056
1057   @Override
1058   public void setProgressBar(String message, long id)
1059   {
1060     progressBar.setProgressBar(message, id);
1061   }
1062
1063   @Override
1064   public void registerHandler(long id, IProgressIndicatorHandler handler)
1065   {
1066     progressBar.registerHandler(id, handler);
1067   }
1068
1069   @Override
1070   public boolean operationInProgress()
1071   {
1072     return progressBar.operationInProgress();
1073   }
1074 }