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