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