package jalview.gui;
+import java.awt.event.ItemEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.concurrent.Executors;
+
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JTable;
+import javax.swing.SwingUtilities;
+import javax.swing.table.AbstractTableModel;
+
import jalview.api.structures.JalviewStructureDisplayI;
import jalview.bin.Cache;
import jalview.bin.Jalview;
-import jalview.datamodel.DBRefEntry;
-import jalview.datamodel.DBRefSource;
import jalview.datamodel.PDBEntry;
import jalview.datamodel.SequenceI;
import jalview.fts.api.FTSData;
import jalview.fts.core.FTSRestRequest;
import jalview.fts.core.FTSRestResponse;
import jalview.fts.service.pdb.PDBFTSRestClient;
+import jalview.gui.structurechooser.PDBStructureChooserQuerySource;
+import jalview.gui.structurechooser.StructureChooserQuerySource;
+import jalview.gui.structurechooser.ThreeDBStructureChooserQuerySource;
import jalview.io.DataSourceType;
+import jalview.jbgui.FilterOption;
import jalview.jbgui.GStructureChooser;
-import jalview.jbgui.GStructureChooser.FilterOption;
import jalview.structure.StructureMapping;
import jalview.structure.StructureSelectionManager;
import jalview.util.MessageManager;
import jalview.ws.DBRefFetcher;
+import jalview.ws.seqfetcher.DbSourceProxy;
import jalview.ws.sifts.SiftsSettings;
-import java.awt.event.ItemEvent;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-import java.util.Vector;
-
-import javax.swing.JCheckBox;
-import javax.swing.JComboBox;
-import javax.swing.JLabel;
-import javax.swing.JTable;
-import javax.swing.SwingUtilities;
-import javax.swing.table.AbstractTableModel;
-
/**
* Provides the behaviors for the Structure chooser Panel
*
{
private static final String AUTOSUPERIMPOSE = "AUTOSUPERIMPOSE";
+ /**
+ * transient combo box choice for initiating 3db fetch
+ */
+ private static final String VIEWS_QUERYING_TDB = "QUERY_3DB";
+
private SequenceI selectedSequence;
private SequenceI[] selectedSequences;
private boolean cachedPDBExists;
+ private Collection<FTSData> lastDiscoveredStructuresSet;
+
+ private boolean canQueryTDB = false;
+
+ private boolean notQueriedTDBYet = true;
+
+ List<SequenceI> seqsWithoutSourceDBRef = null;
+
private static StructureViewer lastTargetedView = null;
public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
AlignmentPanel ap)
{
// which FTS engine to use
- data = StructureChooserQuerySource
- .getPDBfts();
+ data = StructureChooserQuerySource.getQuerySourceFor(selectedSeqs);
initDialog();
-
+
this.ap = ap;
this.selectedSequence = selectedSeq;
this.selectedSequences = selectedSeqs;
this.progressIndicator = (ap == null) ? null : ap.alignFrame;
init();
-
+
}
/**
+ * sets canQueryTDB if protein sequences without a canonical uniprot ref or at
+ * least one structure are discovered.
+ */
+ private void populateSeqsWithoutSourceDBRef()
+ {
+ seqsWithoutSourceDBRef = new ArrayList<SequenceI>();
+ boolean needCanonical = false;
+ for (SequenceI seq : selectedSequences)
+ {
+ if (seq.isProtein())
+ {
+ int dbRef = ThreeDBStructureChooserQuerySource
+ .checkUniprotRefs(seq.getDBRefs());
+ if (dbRef < 0)
+ {
+ if (dbRef == -1)
+ {
+ // need to retrieve canonicals
+ needCanonical = true;
+ seqsWithoutSourceDBRef.add(seq);
+ }
+ else
+ {
+ // could be a sequence with pdb ref
+ if (seq.getAllPDBEntries() == null
+ || seq.getAllPDBEntries().size() == 0)
+ {
+ seqsWithoutSourceDBRef.add(seq);
+ }
+ }
+ }
+ }
+ }
+ // retrieve database refs for protein sequences
+ if (!seqsWithoutSourceDBRef.isEmpty())
+ {
+ canQueryTDB = true;
+ if (needCanonical)
+ {
+ notQueriedTDBYet = false;
+ }
+ }
+ };
+
+ /**
* Initializes parameters used by the Structure Chooser Panel
*/
protected void init()
chk_superpose.setSelected(Cache.getDefault(AUTOSUPERIMPOSE, true));
+ Executors.defaultThreadFactory().newThread(new Runnable()
+ {
+ public void run()
+ {
+ populateSeqsWithoutSourceDBRef();
+ initialStructureDiscovery();
+ }
+
+ }).start();
+
+ }
+
+ // called by init
+ private void initialStructureDiscovery()
+ {
+ // check which FTS engine to use
+ data = StructureChooserQuerySource.getQuerySourceFor(selectedSequences);
+
// ensure a filter option is in force for search
populateFilterComboBox(true, cachedPDBExists);
- Thread discoverPDBStructuresThread = new Thread(new Runnable()
+
+ // looks for any existing structures already loaded
+ // for the sequences (the cached ones)
+ // then queries the StructureChooserQuerySource to
+ // discover more structures.
+ //
+ // Possible optimisation is to only begin querying
+ // the structure chooser if there are no cached structures.
+
+ long startTime = System.currentTimeMillis();
+ updateProgressIndicator(
+ MessageManager.getString("status.loading_cached_pdb_entries"),
+ startTime);
+ loadLocalCachedPDBEntries();
+ updateProgressIndicator(null, startTime);
+ updateProgressIndicator(
+ MessageManager.getString("status.searching_for_pdb_structures"),
+ startTime);
+ fetchStructuresMetaData();
+ // revise filter options if no results were found
+ populateFilterComboBox(isStructuresDiscovered(), cachedPDBExists);
+ discoverStructureViews();
+ updateProgressIndicator(null, startTime);
+ mainFrame.setVisible(true);
+ updateCurrentView();
+ }
+
+ private void promptForTDBFetch()
+ {
+ final long progressId = System.currentTimeMillis();
+
+ // final action after prompting and discovering db refs
+ final Runnable strucDiscovery = new Runnable()
{
@Override
public void run()
{
- long startTime = System.currentTimeMillis();
- updateProgressIndicator(MessageManager
- .getString("status.loading_cached_pdb_entries"), startTime);
- loadLocalCachedPDBEntries();
- updateProgressIndicator(null, startTime);
- updateProgressIndicator(MessageManager.getString(
- "status.searching_for_pdb_structures"), startTime);
- fetchStructuresMetaData();
- // revise filter options if no results were found
- populateFilterComboBox(isStructuresDiscovered(), cachedPDBExists);
- discoverStructureViews();
- updateProgressIndicator(null, startTime);
- mainFrame.setVisible(true);
- updateCurrentView();
+ // TODO: warn if no accessions discovered
+ populateSeqsWithoutSourceDBRef();
+ // redo initial discovery - this time with 3d beacons
+ // Executors.
+ previousWantedFields=null;
+
+ initialStructureDiscovery();
}
- });
- discoverPDBStructuresThread.start();
+ };
+
+ // fetch db refs if OK pressed
+ final Runnable discoverCanonicalDBrefs = new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ populateSeqsWithoutSourceDBRef();
+
+ final int y = seqsWithoutSourceDBRef.size();
+ if (y > 0)
+ {
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run()
+ {
+ updateProgressIndicator(MessageManager.formatMessage(
+ "status.fetching_dbrefs_for_sequences_without_valid_refs",
+ y), progressId);
+
+ }
+ });
+
+ final SequenceI[] seqWithoutSrcDBRef = seqsWithoutSourceDBRef
+ .toArray(new SequenceI[y]);
+ DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef,
+ progressBar, new DbSourceProxy[]
+ { new jalview.ws.dbsources.Uniprot() }, null, false);
+
+ // ideally this would also gracefully run with callbacks
+ dbRefFetcher.fetchDBRefs(true);
+ }
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run()
+ {
+ if (y>0) {
+ updateProgressIndicator("Fetch complete.", progressId); // todo i18n
+ }
+ // filter has been selected, so we set flag to remove ourselves
+ notQueriedTDBYet = false;
+
+ Executors.defaultThreadFactory().newThread(strucDiscovery).start();
+
+ }
+ });
+
+ }
+ };
+ final Runnable revertview = new Runnable() {
+ public void run() {
+ if (lastSelected!=null) {
+ cmb_filterOption.setSelectedItem(lastSelected);
+ }
+ };
+ };
+ // need cancel and no to result in the discoverPDB action - mocked is
+ // 'cancel'
+ JvOptionPane.newOptionDialog(this)
+ .setResponseHandler(JvOptionPane.OK_OPTION,
+ discoverCanonicalDBrefs)
+ .setResponseHandler(JvOptionPane.CANCEL_OPTION, revertview)
+ .setResponseHandler(JvOptionPane.NO_OPTION, revertview)
+ .showDialog(
+ MessageManager.formatMessage(
+ "label.fetch_references_for_3dbeacons",
+ seqsWithoutSourceDBRef.size()),
+ MessageManager
+ .getString("label.3dbeacons"),
+ JvOptionPane.YES_NO_OPTION, JvOptionPane.PLAIN_MESSAGE,
+ null, new Object[]
+ { MessageManager.getString("action.ok"),
+ MessageManager.getString("action.cancel") },
+ MessageManager.getString("action.ok"));
}
/**
{
resultList = data.fetchStructuresMetaData(seq, wantedFields,
selectedFilterOpt, !chk_invertFilter.isSelected());
+ // null response means the FTSengine didn't yield a query for this
+ // consider designing a special exception if we really wanted to be
+ // OOCrazy
+ if (resultList == null)
+ {
+ continue;
+ }
} catch (Exception e)
{
e.printStackTrace();
{
getResultTable()
.setModel(data.getTableModel(discoveredStructuresSet));
+
noOfStructuresFound = discoveredStructuresSet.size();
+ lastDiscoveredStructuresSet = discoveredStructuresSet;
mainFrame.setTitle(MessageManager.formatMessage(
"label.structure_chooser_no_of_structures",
noOfStructuresFound, totalTime));
{
Thread filterThread = new Thread(new Runnable()
{
+
@Override
public void run()
{
FTSRestResponse resultList;
try
{
- resultList = data.selectFirstRankedQuery(seq, wantedFields,
- fieldToFilterBy, !chk_invertFilter.isSelected());
+ resultList = data.selectFirstRankedQuery(seq,
+ discoveredStructuresSet, wantedFields, fieldToFilterBy,
+ !chk_invertFilter.isSelected());
} catch (Exception e)
{
protected void populateFilterComboBox(boolean haveData,
boolean cachedPDBExist)
{
+ populateFilterComboBox(haveData, cachedPDBExist, null);
+ }
+
+ /**
+ * Populates the filter combo-box options dynamically depending on discovered
+ * structures
+ */
+ protected void populateFilterComboBox(boolean haveData,
+ boolean cachedPDBExist, FilterOption lastSel)
+ {
+
/*
* temporarily suspend the change listener behaviour
*/
cmb_filterOption.removeItemListener(this);
-
+ int selSet = -1;
cmb_filterOption.removeAllItems();
if (haveData)
{
- cmb_filterOption.addItem(new FilterOption(
- MessageManager.getString("label.best_quality"),
- "overall_quality", VIEWS_FILTER, false));
- cmb_filterOption.addItem(new FilterOption(
- MessageManager.getString("label.best_resolution"),
- "resolution", VIEWS_FILTER, false));
- cmb_filterOption.addItem(new FilterOption(
- MessageManager.getString("label.most_protein_chain"),
- "number_of_protein_chains", VIEWS_FILTER, false));
- cmb_filterOption.addItem(new FilterOption(
- MessageManager.getString("label.most_bound_molecules"),
- "number_of_bound_molecules", VIEWS_FILTER, false));
- cmb_filterOption.addItem(new FilterOption(
- MessageManager.getString("label.most_polymer_residues"),
- "number_of_polymer_residues", VIEWS_FILTER, true));
+ List<FilterOption> filters = data
+ .getAvailableFilterOptions(VIEWS_FILTER);
+ data.updateAvailableFilterOptions(VIEWS_FILTER, filters,
+ lastDiscoveredStructuresSet);
+ int p = 0;
+ for (FilterOption filter : filters)
+ {
+ if (lastSel != null && filter.equals(lastSel))
+ {
+ selSet = p;
+ }
+ p++;
+ cmb_filterOption.addItem(filter);
+ }
}
+
cmb_filterOption.addItem(
new FilterOption(MessageManager.getString("label.enter_pdb_id"),
- "-", VIEWS_ENTER_ID, false));
+ "-", VIEWS_ENTER_ID, false, null));
cmb_filterOption.addItem(
new FilterOption(MessageManager.getString("label.from_file"),
- "-", VIEWS_FROM_FILE, false));
+ "-", VIEWS_FROM_FILE, false, null));
+ if (canQueryTDB && notQueriedTDBYet)
+ {
+ FilterOption queryTDBOption = new FilterOption(
+ MessageManager.getString("label.search_3dbeacons"), "-",
+ VIEWS_QUERYING_TDB, false, null);
+ cmb_filterOption.addItem(queryTDBOption);
+ }
if (cachedPDBExist)
{
FilterOption cachedOption = new FilterOption(
MessageManager.getString("label.cached_structures"), "-",
- VIEWS_LOCAL_PDB, false);
+ VIEWS_LOCAL_PDB, false, null);
cmb_filterOption.addItem(cachedOption);
- cmb_filterOption.setSelectedItem(cachedOption);
+ if (selSet == -1)
+ {
+ cmb_filterOption.setSelectedItem(cachedOption);
+ }
+ }
+ if (selSet > -1)
+ {
+ cmb_filterOption.setSelectedIndex(selSet);
}
-
cmb_filterOption.addItemListener(this);
}
{
FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
.getSelectedItem());
+
+ // first check if we need to rebuild dialog
+ if (selectedFilterOpt.getView() == VIEWS_QUERYING_TDB)
+ {
+ promptForTDBFetch();
+ return;
+ }
+ if (lastSelected == selectedFilterOpt)
+ {
+ // don't need to do anything, probably
+ return;
+ }
+ // otherwise, record selection
+ // and update the layout and dialog accordingly
+ lastSelected = selectedFilterOpt;
+
layout_switchableViews.show(pnl_switchableViews,
selectedFilterOpt.getView());
String filterTitle = mainFrame.getTitle();
mainFrame.setTitle(frameTitle);
chk_invertFilter.setVisible(false);
+
if (selectedFilterOpt.getView() == VIEWS_FILTER)
{
mainFrame.setTitle(filterTitle);
- chk_invertFilter.setVisible(true);
- filterResultSet(selectedFilterOpt.getValue());
+ // TDB Query has no invert as yet
+ chk_invertFilter.setVisible(selectedFilterOpt
+ .getQuerySource() instanceof PDBStructureChooserQuerySource);
+
+ if (data != selectedFilterOpt.getQuerySource()
+ || data.needsRefetch(selectedFilterOpt))
+ {
+ data = selectedFilterOpt.getQuerySource();
+ // rebuild the views completely, since prefs will also change
+ tabRefresh();
+ return;
+ }
+ else
+ {
+ filterResultSet(selectedFilterOpt.getValue());
+ }
}
else if (selectedFilterOpt.getView() == VIEWS_ENTER_ID
|| selectedFilterOpt.getView() == VIEWS_FROM_FILE)
{
validateSelections();
}
-
+ private FilterOption lastSelected=null;
/**
* Handles the state change event for the 'filter' combo-box and 'invert'
* check-box
if (currentView == VIEWS_FILTER)
{
- int pdbIdColIndex = restable.getColumn("PDB Id").getModelIndex();
- int refSeqColIndex = restable.getColumn("Ref Sequence")
- .getModelIndex();
int[] selectedRows = restable.getSelectedRows();
PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
- int count = 0;
List<SequenceI> selectedSeqsToView = new ArrayList<>();
- for (int row : selectedRows)
- {
- String pdbIdStr = restable.getValueAt(row, pdbIdColIndex)
- .toString();
- SequenceI selectedSeq = (SequenceI) restable.getValueAt(row,
- refSeqColIndex);
- selectedSeqsToView.add(selectedSeq);
- PDBEntry pdbEntry = selectedSeq.getPDBEntry(pdbIdStr);
- if (pdbEntry == null)
- {
- pdbEntry = getFindEntry(pdbIdStr,
- selectedSeq.getAllPDBEntries());
- }
+ pdbEntriesToView = data.collectSelectedRows(restable,
+ selectedRows, selectedSeqsToView);
- if (pdbEntry == null)
- {
- pdbEntry = new PDBEntry();
- pdbEntry.setId(pdbIdStr);
- pdbEntry.setType(PDBEntry.Type.PDB);
- selectedSeq.getDatasetSequence().addPDBId(pdbEntry);
- }
- pdbEntriesToView[count++] = pdbEntry;
- }
SequenceI[] selectedSeqs = selectedSeqsToView
.toArray(new SequenceI[selectedSeqsToView.size()]);
sViewer = launchStructureViewer(ssm, pdbEntriesToView, ap,
List<SequenceI> selectedSeqsToView = new ArrayList<>();
for (int row : selectedRows)
{
- PDBEntry pdbEntry = (PDBEntry) tbl_local_pdb.getValueAt(row,
- pdbIdColIndex);
+ PDBEntry pdbEntry = ((PDBEntryTableModel) tbl_local_pdb
+ .getModel()).getPDBEntryAt(row).getPdbEntry();
+
pdbEntriesToView[count++] = pdbEntry;
SequenceI selectedSeq = (SequenceI) tbl_local_pdb
.getValueAt(row, refSeqColIndex);
}
}
- private PDBEntry getFindEntry(String id, Vector<PDBEntry> pdbEntries)
- {
- Objects.requireNonNull(id);
- Objects.requireNonNull(pdbEntries);
- PDBEntry foundEntry = null;
- for (PDBEntry entry : pdbEntries)
- {
- if (entry.getId().equalsIgnoreCase(id))
- {
- return entry;
- }
- }
- return foundEntry;
- }
-
/**
* Answers a structure viewer (new or existing) configured to superimpose
* added structures or not according to the user's choice
public void run()
{
fetchStructuresMetaData();
+ // populateFilterComboBox(true, cachedPDBExists);
+
filterResultSet(
((FilterOption) cmb_filterOption.getSelectedItem())
.getValue());
value = entry.getSequence();
break;
case 1:
- value = entry.getPdbEntry();
+ value = entry.getQualifiedId();
break;
case 2:
value = entry.getPdbEntry().getChainCode() == null ? "_"
this.pdbEntry = pdbEntry;
}
+ public String getQualifiedId()
+ {
+ if (pdbEntry.hasProvider())
+ {
+ return pdbEntry.getProvider() + ":" + pdbEntry.getId();
+ }
+ return pdbEntry.toString();
+ }
+
public SequenceI getSequence()
{
return sequence;
protected void setFTSDocFieldPrefs(FTSDataColumnPreferences newPrefs)
{
data.setDocFieldPrefs(newPrefs);
-
+
+ }
+
+ /**
+ *
+ * @return true when all initialisation threads have finished and dialog is
+ * visible
+ */
+ public boolean isDialogVisible()
+ {
+ return mainFrame != null && data != null && cmb_filterOption != null
+ && mainFrame.isVisible()
+ && cmb_filterOption.getSelectedItem() != null;
}
}