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