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