JAL-1645 source formatting and organise imports
[jalview.git] / src / jalview / gui / StructureChooser.java
1 /*
2
3  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
4  * Copyright (C) 2014 The Jalview Authors
5  * 
6  * This file is part of Jalview.
7  * 
8  * Jalview is free software: you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License 
10  * as published by the Free Software Foundation, either version 3
11  * of the License, or (at your option) any later version.
12  *  
13  * Jalview is distributed in the hope that it will be useful, but 
14  * WITHOUT ANY WARRANTY; without even the implied warranty 
15  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
16  * PURPOSE.  See the GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
20  * The Jalview Authors are detailed in the 'AUTHORS' file.
21  */
22
23 package jalview.gui;
24
25 import jalview.datamodel.DBRefEntry;
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("(text:");
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     HashSet<String> seqRefs = new LinkedHashSet<String>();
238     String seqName = seq.getName();
239     String[] names = seqName.toLowerCase().split("\\|");
240     for (String name : names)
241     {
242       // System.out.println("Found name : " + name);
243       name.trim();
244       if (isValidSeqName(name))
245       {
246         seqRefs.add(name);
247       }
248     }
249
250     if (seq.getAllPDBEntries() != null)
251     {
252       for (PDBEntry entry : seq.getAllPDBEntries())
253       {
254         if (isValidSeqName(entry.getId()))
255         {
256           seqRefs.add(entry.getId());
257         }
258       }
259     }
260
261     if (seq.getDBRef() != null && seq.getDBRef().length != 0)
262     {
263       int count = 0;
264       for (DBRefEntry dbRef : seq.getDBRef())
265       {
266         if (isValidSeqName(getDBRefId(dbRef)))
267         {
268           seqRefs.add(getDBRefId(dbRef));
269         }
270         ++count;
271         if (count > 10)
272         {
273           break;
274         }
275       }
276     }
277
278     StringBuilder queryBuilder = new StringBuilder();
279     for (String seqRef : seqRefs)
280     {
281       queryBuilder.append("text:").append(seqRef).append(" OR ");
282     }
283     int endIndex = queryBuilder.lastIndexOf(" OR ");
284
285     if (queryBuilder.toString().length() < 6)
286     {
287       return null;
288     }
289     String query = queryBuilder.toString().substring(5, endIndex);
290     return query;
291   }
292
293   /**
294    * Ensures sequence ref names are not less than 3 characters and does not
295    * contain a database name
296    * 
297    * @param seqName
298    * @return
299    */
300   public static boolean isValidSeqName(String seqName)
301   {
302     // System.out.println("seqName : " + seqName);
303     String ignoreList = "pdb,uniprot,swiss-prot";
304     if (seqName.length() < 3)
305     {
306       return false;
307     }
308     if (seqName.contains(":"))
309     {
310       return false;
311     }
312     seqName = seqName.toLowerCase();
313     for (String ignoredEntry : ignoreList.split(","))
314     {
315       if (seqName.contains(ignoredEntry))
316       {
317         return false;
318       }
319     }
320     return true;
321   }
322
323   public static String getDBRefId(DBRefEntry dbRef)
324   {
325     String ref = dbRef.getAccessionId().replaceAll("GO:", "");
326     return ref;
327   }
328
329   /**
330    * Filters a given list of discovered structures based on supplied argument
331    * 
332    * @param fieldToFilterBy
333    *          the field to filter by
334    */
335   public void filterResultSet(final String fieldToFilterBy)
336   {
337     Thread filterThread = new Thread(new Runnable()
338     {
339       @Override
340       public void run()
341       {
342         long startTime = System.currentTimeMillis();
343         lbl_loading.setVisible(true);
344         Collection<PDBDocField> wantedFields = PDBDocFieldPreferences
345                 .getStructureSummaryFields();
346         Collection<PDBResponseSummary> filteredResponse = new HashSet<PDBResponseSummary>();
347         HashSet<String> errors = new HashSet<String>();
348         for (SequenceI seq : selectedSequences)
349         {
350           PDBRestRequest pdbRequest = new PDBRestRequest();
351           pdbRequest.setAllowEmptySeq(false);
352           pdbRequest.setResponseSize(1);
353           pdbRequest.setFieldToSearchBy("(text:");
354           pdbRequest.setFieldToSortBy(fieldToFilterBy,
355                   !chk_invertFilter.isSelected());
356           pdbRequest.setSearchTerm(buildQuery(seq) + ")");
357           pdbRequest.setWantedFields(wantedFields);
358           pdbRequest.setAssociatedSequence(seq);
359           pdbRestCleint = new PDBRestClient();
360           PDBRestResponse resultList;
361           try
362           {
363             resultList = pdbRestCleint.executeRequest(pdbRequest);
364           } catch (Exception e)
365           {
366             e.printStackTrace();
367             errors.add(e.getMessage());
368             continue;
369           }
370           lastPdbRequest = pdbRequest;
371           if (resultList.getSearchSummary() != null
372                   && !resultList.getSearchSummary().isEmpty())
373           {
374             filteredResponse.addAll(resultList.getSearchSummary());
375           }
376         }
377
378         String totalTime = (System.currentTimeMillis() - startTime)
379                 + " milli secs";
380         if (!filteredResponse.isEmpty())
381         {
382           final int filterResponseCount = filteredResponse.size();
383           Collection<PDBResponseSummary> reorderedStructuresSet = new LinkedHashSet<PDBResponseSummary>();
384           reorderedStructuresSet.addAll(filteredResponse);
385           reorderedStructuresSet.addAll(discoveredStructuresSet);
386           tbl_summary.setModel(PDBRestResponse.getTableModel(
387                   lastPdbRequest, reorderedStructuresSet));
388
389           // Update table selection model here
390           tbl_summary.addRowSelectionInterval(0, filterResponseCount - 1);
391           mainFrame.setTitle(MessageManager.formatMessage(
392                   "label.structure_chooser_filter_time", totalTime));
393         }
394         else
395         {
396           mainFrame.setTitle(MessageManager.formatMessage(
397                   "label.structure_chooser_filter_time", totalTime));
398           if (errors.size() > 0)
399           {
400             StringBuilder errorMsg = new StringBuilder();
401             for (String error : errors)
402             {
403               errorMsg.append(error).append("\n");
404             }
405             JOptionPane.showMessageDialog(
406                     null,
407                     errorMsg.toString(),
408                     MessageManager.getString("label.pdb_web-service_error"),
409                     JOptionPane.ERROR_MESSAGE);
410           }
411         }
412
413         lbl_loading.setVisible(false);
414
415         validateSelections();
416       }
417     });
418     filterThread.start();
419   }
420
421   /**
422    * Handles action event for btn_pdbFromFile
423    */
424   public void pdbFromFile_actionPerformed()
425   {
426     jalview.io.JalviewFileChooser chooser = new jalview.io.JalviewFileChooser(
427             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
428     chooser.setFileView(new jalview.io.JalviewFileView());
429     chooser.setDialogTitle(MessageManager.formatMessage(
430             "label.select_pdb_file_for",
431             selectedSequence.getDisplayId(false)));
432     chooser.setToolTipText(MessageManager.formatMessage(
433             "label.load_pdb_file_associate_with_sequence",
434             selectedSequence.getDisplayId(false)));
435
436     int value = chooser.showOpenDialog(null);
437     if (value == jalview.io.JalviewFileChooser.APPROVE_OPTION)
438     {
439       selectedPdbFileName = chooser.getSelectedFile().getPath();
440       jalview.bin.Cache.setProperty("LAST_DIRECTORY", selectedPdbFileName);
441       validateSelections();
442     }
443   }
444
445   /**
446    * Populates the filter combo-box options dynamically depending on discovered
447    * structures
448    */
449   protected void populateFilterComboBox()
450   {
451     if (isStructuresDiscovered())
452     {
453       cmb_filterOption.addItem(new FilterOption("Best Quality",
454               PDBDocField.OVERALL_QUALITY.getCode(), VIEWS_FILTER));
455       cmb_filterOption.addItem(new FilterOption("Best UniProt Coverage",
456               PDBDocField.UNIPROT_COVERAGE.getCode(), VIEWS_FILTER));
457       cmb_filterOption.addItem(new FilterOption("Highest Resolution",
458               PDBDocField.RESOLUTION.getCode(), VIEWS_FILTER));
459       cmb_filterOption.addItem(new FilterOption("Highest Protein Chain",
460               PDBDocField.PROTEIN_CHAIN_COUNT.getCode(), VIEWS_FILTER));
461       cmb_filterOption.addItem(new FilterOption("Highest Bound Molecules",
462               PDBDocField.BOUND_MOLECULE_COUNT.getCode(), VIEWS_FILTER));
463       cmb_filterOption.addItem(new FilterOption("Highest Polymer Residues",
464               PDBDocField.POLYMER_RESIDUE_COUNT.getCode(), VIEWS_FILTER));
465     }
466     cmb_filterOption.addItem(new FilterOption("Enter PDB Id", "-",
467             VIEWS_ENTER_ID));
468     cmb_filterOption.addItem(new FilterOption("From File", "-",
469             VIEWS_FROM_FILE));
470     cmb_filterOption.addItem(new FilterOption("Cached PDB Entries", "-",
471             VIEWS_LOCAL_PDB));
472   }
473
474   /**
475    * Updates the displayed view based on the selected filter option
476    */
477   protected void updateCurrentView()
478   {
479     FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
480             .getSelectedItem());
481     layout_switchableViews.show(pnl_switchableViews,
482             selectedFilterOpt.getView());
483     String filterTitle = mainFrame.getTitle();
484     mainFrame.setTitle(frameTitle);
485     chk_invertFilter.setVisible(false);
486     if (selectedFilterOpt.getView() == VIEWS_FILTER)
487     {
488       mainFrame.setTitle(filterTitle);
489       chk_invertFilter.setVisible(true);
490       filterResultSet(selectedFilterOpt.getValue());
491     }
492     else if (selectedFilterOpt.getView() == VIEWS_ENTER_ID
493             || selectedFilterOpt.getView() == VIEWS_FROM_FILE)
494     {
495       mainFrame.setTitle(MessageManager
496               .getString("label.structure_chooser_manual_association"));
497       idInputAssSeqPanel.loadCmbAssSeq();
498       fileChooserAssSeqPanel.loadCmbAssSeq();
499     }
500     validateSelections();
501   }
502
503   /**
504    * Validates user selection and activates the view button if all parameters
505    * are correct
506    */
507   public void validateSelections()
508   {
509     FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
510             .getSelectedItem());
511     btn_view.setEnabled(false);
512     String currentView = selectedFilterOpt.getView();
513     if (currentView == VIEWS_FILTER)
514     {
515       if (tbl_summary.getSelectedRows().length > 0)
516       {
517         btn_view.setEnabled(true);
518       }
519     }
520     else if (currentView == VIEWS_LOCAL_PDB)
521     {
522       if (tbl_local_pdb.getSelectedRows().length > 0)
523       {
524         btn_view.setEnabled(true);
525       }
526     }
527     else if (currentView == VIEWS_ENTER_ID)
528     {
529       validateAssociationEnterPdb();
530     }
531     else if (currentView == VIEWS_FROM_FILE)
532     {
533       validateAssociationFromFile();
534     }
535   }
536
537   /**
538    * Validates inputs from the Manual PDB entry panel
539    */
540   public void validateAssociationEnterPdb()
541   {
542     AssociateSeqOptions assSeqOpt = (AssociateSeqOptions) idInputAssSeqPanel
543             .getCmb_assSeq().getSelectedItem();
544     lbl_pdbManualFetchStatus.setIcon(errorImage);
545     lbl_pdbManualFetchStatus.setToolTipText("");
546     if (txt_search.getText().length() > 0)
547     {
548       lbl_pdbManualFetchStatus
549               .setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager
550                       .formatMessage("info.no_pdb_entry_found_for",
551                               txt_search.getText())));
552     }
553
554     if (errorWarning.length() > 0)
555     {
556       lbl_pdbManualFetchStatus.setIcon(warningImage);
557       lbl_pdbManualFetchStatus.setToolTipText(JvSwingUtils.wrapTooltip(
558               true, errorWarning.toString()));
559     }
560
561     if (selectedSequences.length == 1
562             || !assSeqOpt.getName().equalsIgnoreCase(
563                     "-Select Associated Seq-"))
564     {
565       txt_search.setEnabled(true);
566       if (isValidPBDEntry)
567       {
568         btn_view.setEnabled(true);
569         lbl_pdbManualFetchStatus.setToolTipText("");
570         lbl_pdbManualFetchStatus.setIcon(goodImage);
571       }
572     }
573     else
574     {
575       txt_search.setEnabled(false);
576       lbl_pdbManualFetchStatus.setIcon(errorImage);
577     }
578   }
579
580   /**
581    * Validates inputs for the manual PDB file selection options
582    */
583   public void validateAssociationFromFile()
584   {
585     AssociateSeqOptions assSeqOpt = (AssociateSeqOptions) fileChooserAssSeqPanel
586             .getCmb_assSeq().getSelectedItem();
587     lbl_fromFileStatus.setIcon(errorImage);
588     if (selectedSequences.length == 1
589             || (assSeqOpt != null && !assSeqOpt.getName().equalsIgnoreCase(
590                     "-Select Associated Seq-")))
591     {
592       btn_pdbFromFile.setEnabled(true);
593       if (selectedPdbFileName != null && selectedPdbFileName.length() > 0)
594       {
595         btn_view.setEnabled(true);
596         lbl_fromFileStatus.setIcon(goodImage);
597       }
598     }
599     else
600     {
601       btn_pdbFromFile.setEnabled(false);
602       lbl_fromFileStatus.setIcon(errorImage);
603     }
604   }
605
606   @Override
607   public void cmbAssSeqStateChanged()
608   {
609     validateSelections();
610   }
611
612   /**
613    * Handles the state change event for the 'filter' combo-box and 'invert'
614    * check-box
615    */
616   @Override
617   protected void stateChanged(ItemEvent e)
618   {
619     if (e.getSource() instanceof JCheckBox)
620     {
621       updateCurrentView();
622     }
623     else
624     {
625       if (e.getStateChange() == ItemEvent.SELECTED)
626       {
627         updateCurrentView();
628       }
629     }
630
631   }
632
633   /**
634    * Handles action event for btn_ok
635    */
636   @Override
637   public void ok_ActionPerformed()
638   {
639     FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
640             .getSelectedItem());
641     String currentView = selectedFilterOpt.getView();
642     if (currentView == VIEWS_FILTER)
643     {
644       int pdbIdColIndex = tbl_summary.getColumn(
645               PDBRestClient.PDBDocField.PDB_ID.getName()).getModelIndex();
646       int refSeqColIndex = tbl_summary.getColumn("Ref Sequence")
647               .getModelIndex();
648       int[] selectedRows = tbl_summary.getSelectedRows();
649       PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
650       int count = 0;
651       ArrayList<SequenceI> selectedSeqsToView = new ArrayList<SequenceI>();
652       for (int row : selectedRows)
653       {
654         String pdbIdStr = tbl_summary.getValueAt(row, pdbIdColIndex)
655                 .toString();
656         SequenceI selectedSeq = (SequenceI) tbl_summary.getValueAt(row,
657                 refSeqColIndex);
658         selectedSeqsToView.add(selectedSeq);
659         PDBEntry pdbEntry = selectedSeq.getPDBEntry(pdbIdStr);
660         if (pdbEntry == null)
661         {
662           pdbEntry = new PDBEntry();
663           pdbEntry.setId(pdbIdStr);
664           pdbEntry.setType(PDBEntry.Type.PDB);
665           selectedSeq.getDatasetSequence().addPDBId(pdbEntry);
666         }
667         pdbEntriesToView[count++] = pdbEntry;
668       }
669       SequenceI[] selectedSeqs = selectedSeqsToView
670               .toArray(new SequenceI[selectedSeqsToView.size()]);
671       launchStructureViewer(ap.getStructureSelectionManager(),
672               pdbEntriesToView, ap, selectedSeqs);
673     }
674     else if (currentView == VIEWS_LOCAL_PDB)
675     {
676       int[] selectedRows = tbl_local_pdb.getSelectedRows();
677       PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
678       int count = 0;
679       int pdbIdColIndex = tbl_local_pdb.getColumn(
680               PDBRestClient.PDBDocField.PDB_ID.getName()).getModelIndex();
681       int refSeqColIndex = tbl_local_pdb.getColumn("Ref Sequence")
682               .getModelIndex();
683       ArrayList<SequenceI> selectedSeqsToView = new ArrayList<SequenceI>();
684       for (int row : selectedRows)
685       {
686         PDBEntry pdbEntry = (PDBEntry) tbl_local_pdb.getValueAt(row,
687                 pdbIdColIndex);
688         pdbEntriesToView[count++] = pdbEntry;
689         SequenceI selectedSeq = (SequenceI) tbl_local_pdb.getValueAt(row,
690                 refSeqColIndex);
691         selectedSeqsToView.add(selectedSeq);
692       }
693       SequenceI[] selectedSeqs = selectedSeqsToView
694               .toArray(new SequenceI[selectedSeqsToView.size()]);
695       launchStructureViewer(ap.getStructureSelectionManager(),
696               pdbEntriesToView, ap, selectedSeqs);
697     }
698     else if (currentView == VIEWS_ENTER_ID)
699     {
700       SequenceI userSelectedSeq = ((AssociateSeqOptions) idInputAssSeqPanel
701               .getCmb_assSeq().getSelectedItem()).getSequence();
702       if (userSelectedSeq != null)
703       {
704         selectedSequence = userSelectedSeq;
705       }
706
707       String pdbIdStr = txt_search.getText();
708       PDBEntry pdbEntry = selectedSequence.getPDBEntry(pdbIdStr);
709       if (pdbEntry == null)
710       {
711         pdbEntry = new PDBEntry();
712         pdbEntry.setId(pdbIdStr);
713         pdbEntry.setType(PDBEntry.Type.PDB);
714         selectedSequence.getDatasetSequence().addPDBId(pdbEntry);
715       }
716
717       PDBEntry[] pdbEntriesToView = new PDBEntry[] { pdbEntry };
718       launchStructureViewer(ap.getStructureSelectionManager(),
719               pdbEntriesToView, ap, new SequenceI[] { selectedSequence });
720     }
721     else if (currentView == VIEWS_FROM_FILE)
722     {
723       SequenceI userSelectedSeq = ((AssociateSeqOptions) fileChooserAssSeqPanel
724               .getCmb_assSeq().getSelectedItem()).getSequence();
725       if (userSelectedSeq != null)
726       {
727         selectedSequence = userSelectedSeq;
728       }
729       PDBEntry fileEntry = new AssociatePdbFileWithSeq()
730               .associatePdbWithSeq(selectedPdbFileName,
731                       jalview.io.AppletFormatAdapter.FILE,
732                       selectedSequence, true, Desktop.instance);
733
734       launchStructureViewer(ap.getStructureSelectionManager(),
735               new PDBEntry[] { fileEntry }, ap,
736               new SequenceI[] { selectedSequence });
737     }
738     mainFrame.dispose();
739   }
740
741   private void launchStructureViewer(final StructureSelectionManager ssm,
742           final PDBEntry[] pdbEntriesToView,
743           final AlignmentPanel alignPanel, final SequenceI[] sequences)
744   {
745     final StructureViewer sViewer = new StructureViewer(ssm);
746     new Thread(new Runnable()
747     {
748       public void run()
749       {
750         if (pdbEntriesToView.length > 1)
751         {
752           ArrayList<SequenceI[]> seqsMap = new ArrayList<SequenceI[]>();
753           for (SequenceI seq : sequences)
754           {
755             seqsMap.add(new SequenceI[] { seq });
756           }
757           SequenceI[][] collatedSeqs = seqsMap.toArray(new SequenceI[0][0]);
758           sViewer.viewStructures(pdbEntriesToView, collatedSeqs, alignPanel);
759         }
760         else
761         {
762           sViewer.viewStructures(pdbEntriesToView[0], sequences, alignPanel);
763         }
764       }
765     }).start();
766   }
767
768   /**
769    * Populates the combo-box used in associating manually fetched structures to
770    * a unique sequence when more than one sequence selection is made.
771    */
772   public void populateCmbAssociateSeqOptions(
773           JComboBox<AssociateSeqOptions> cmb_assSeq, JLabel lbl_associateSeq)
774   {
775     cmb_assSeq.removeAllItems();
776     cmb_assSeq.addItem(new AssociateSeqOptions("-Select Associated Seq-",
777             null));
778     lbl_associateSeq.setVisible(false);
779     if (selectedSequences.length > 1)
780     {
781       for (SequenceI seq : selectedSequences)
782       {
783         cmb_assSeq.addItem(new AssociateSeqOptions(seq));
784       }
785     }
786     else
787     {
788       String seqName = selectedSequence.getDisplayId(false);
789       seqName = seqName.length() <= 40 ? seqName : seqName.substring(0, 39);
790       lbl_associateSeq.setText(seqName);
791       lbl_associateSeq.setVisible(true);
792       cmb_assSeq.setVisible(false);
793     }
794   }
795
796   public boolean isStructuresDiscovered()
797   {
798     return structuresDiscovered;
799   }
800
801   public void setStructuresDiscovered(boolean structuresDiscovered)
802   {
803     this.structuresDiscovered = structuresDiscovered;
804   }
805
806   public Collection<PDBResponseSummary> getDiscoveredStructuresSet()
807   {
808     return discoveredStructuresSet;
809   }
810
811   @Override
812   protected void txt_search_ActionPerformed()
813   {
814     new Thread()
815     {
816       public void run()
817       {
818         errorWarning.setLength(0);
819         isValidPBDEntry = false;
820         if (txt_search.getText().length() > 0)
821         {
822           List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
823           wantedFields.add(PDBDocField.PDB_ID);
824           PDBRestRequest pdbRequest = new PDBRestRequest();
825           pdbRequest.setAllowEmptySeq(false);
826           pdbRequest.setResponseSize(1);
827           pdbRequest.setFieldToSearchBy("(pdb_id:");
828           pdbRequest.setWantedFields(wantedFields);
829           pdbRequest.setSearchTerm(txt_search.getText() + ")");
830           pdbRequest.setAssociatedSequence(selectedSequence);
831           pdbRestCleint = new PDBRestClient();
832           PDBRestResponse resultList;
833           try
834           {
835             resultList = pdbRestCleint.executeRequest(pdbRequest);
836           } catch (Exception e)
837           {
838             errorWarning.append(e.getMessage());
839             return;
840           } finally
841           {
842             validateSelections();
843           }
844           if (resultList.getSearchSummary() != null
845                   && resultList.getSearchSummary().size() > 0)
846           {
847             isValidPBDEntry = true;
848           }
849         }
850         validateSelections();
851       }
852     }.start();
853   }
854
855   @Override
856   public void tabRefresh()
857   {
858     if (selectedSequences != null)
859     {
860       Thread refreshThread = new Thread(new Runnable()
861       {
862         @Override
863         public void run()
864         {
865           fetchStructuresMetaData();
866           filterResultSet(((FilterOption) cmb_filterOption
867                   .getSelectedItem()).getValue());
868         }
869       });
870       refreshThread.start();
871     }
872   }
873
874   public class PDBEntryTableModel extends AbstractTableModel
875   {
876     String[] columns = { "Ref Sequence", "PDB Id", "Chain", "Type", "File" };
877
878     private List<CachedPDB> pdbEntries;
879
880     public PDBEntryTableModel(List<CachedPDB> pdbEntries)
881     {
882       this.pdbEntries = new ArrayList<CachedPDB>(pdbEntries);
883     }
884
885     @Override
886     public String getColumnName(int columnIndex)
887     {
888       return columns[columnIndex];
889     }
890
891     @Override
892     public int getRowCount()
893     {
894       return pdbEntries.size();
895     }
896
897     @Override
898     public int getColumnCount()
899     {
900       return columns.length;
901     }
902
903     @Override
904     public boolean isCellEditable(int row, int column)
905     {
906       return false;
907     }
908
909     @Override
910     public Object getValueAt(int rowIndex, int columnIndex)
911     {
912       Object value = "??";
913       CachedPDB entry = pdbEntries.get(rowIndex);
914       switch (columnIndex)
915       {
916       case 0:
917         value = entry.getSequence();
918         break;
919       case 1:
920         value = entry.getPdbEntry();
921         break;
922       case 2:
923         value = entry.getPdbEntry().getChainCode() == null ? "_" : entry
924                 .getPdbEntry().getChainCode();
925         break;
926       case 3:
927         value = entry.getPdbEntry().getType();
928         break;
929       case 4:
930         value = entry.getPdbEntry().getFile();
931         break;
932       }
933       return value;
934     }
935
936     @Override
937     public Class<?> getColumnClass(int columnIndex)
938     {
939       return columnIndex == 0 ? SequenceI.class : PDBEntry.class;
940     }
941
942     public CachedPDB getPDBEntryAt(int row)
943     {
944       return pdbEntries.get(row);
945     }
946
947   }
948
949   private class CachedPDB
950   {
951     private SequenceI sequence;
952
953     private PDBEntry pdbEntry;
954
955     public CachedPDB(SequenceI sequence, PDBEntry pdbEntry)
956     {
957       this.sequence = sequence;
958       this.pdbEntry = pdbEntry;
959     }
960
961     public SequenceI getSequence()
962     {
963       return sequence;
964     }
965
966     public PDBEntry getPdbEntry()
967     {
968       return pdbEntry;
969     }
970
971   }
972 }