import jalview.ws.seqfetcher.DbSourceProxy;
import java.awt.BorderLayout;
+import java.awt.Component;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JTextArea;
import javax.swing.SwingConstants;
+import javajs.async.AsyncSwingWorker;
+
/**
* A panel where the use may choose a database source, and enter one or more
* accessions, to retrieve entries from the database.
* If the selected source is Uniprot or PDB, a free text search panel is opened
* instead to perform the search and selection.
*/
-public class SequenceFetcher extends JPanel implements Runnable
+@SuppressWarnings("serial")
+public class SequenceFetcher extends JPanel
{
- private static jalview.ws.SequenceFetcher sfetch = null;
JLabel exampleAccession;
volatile boolean _isConstructing = false;
/**
- * Returns the shared instance of the SequenceFetcher client
- *
- * @return
- */
- public static jalview.ws.SequenceFetcher getSequenceFetcherSingleton()
- {
- if (sfetch == null)
- {
- sfetch = new jalview.ws.SequenceFetcher();
- }
- return sfetch;
- }
-
- /**
* Constructor given a client to receive any status or progress messages
* (currently either the Desktop, or an AlignFrame panel)
*
final String selectedDb, final String queryString)
{
this.progressIndicator = guiIndic;
- getSequenceFetcherSingleton();
+
this.guiWindow = progressIndicator;
if (progressIndicator instanceof AlignFrame)
frame = new JInternalFrame();
frame.setContentPane(this);
- Desktop.addInternalFrame(frame, getFrameTitle(), true, 400,
- Platform.isAMacAndNotJS() ? 240 : 180);
+ Desktop.addInternalFrame(frame, getFrameTitle(), true, 400,
+ Platform.isAMacAndNotJS() ? 240 : 180);
}
private String getFrameTitle()
database = new JComboBox<>();
database.setFont(JvSwingUtils.getLabelFont());
database.setPrototypeDisplayValue("ENSEMBLGENOMES ");
- String[] sources = new jalview.ws.SequenceFetcher().getSupportedDb();
+ String[] sources = jalview.ws.SequenceFetcher.getInstance()
+ .getSupportedDb();
Arrays.sort(sources, String.CASE_INSENSITIVE_ORDER);
database.addItem(MessageManager.getString("action.select_ddbb"));
for (String source : sources)
{
StringBuilder sb = new StringBuilder();
HashSet<String> hs = new HashSet<>();
- for (DbSourceProxy dbs : sfetch.getSourceProxy(db))
+ for (DbSourceProxy dbs : jalview.ws.SequenceFetcher.getInstance()
+ .getSourceProxy(db))
{
String tq = dbs.getTestQuery();
if (hs.add(tq)) // not a duplicate source
text = text.replace(",", ";");
}
text = text.replaceAll("(\\s|[; ])+", ";");
- if (!t0.equals(text))
+ if (!t0.equals(text))
{
- textArea.setText(text);
+ textArea.setText(text);
}
if (text.isEmpty())
{
resetDialog();
return;
}
- if (database.getSelectedIndex() == 0)
- {
- // todo i18n
- showErrorMessage("Please choose a database");
- resetDialog();
- return;
- }
-
exampleBtn.setEnabled(false);
textArea.setEnabled(false);
okBtn.setEnabled(false);
closeBtn.setEnabled(false);
backBtn.setEnabled(false);
+ fetch(null, true);
+ }
- Thread worker = new Thread(this);
- worker.start();
+ public void fetch(String ids, boolean isAsync)
+ {
+ if (ids == null)
+ {
+ ids = textArea.getText();
+ }
+ else
+ {
+ textArea.setText(ids);
+ }
+ Component parent = null; // or this
+ String title = null; // or some title for the progress monitor
+ int min = AsyncFetchTask.STATE_INIT;
+ int max = AsyncFetchTask.STATE_DONE;
+ int msDelay = (isAsync ? 5 : 0);
+ new AsyncFetchTask(ids, parent, title, msDelay, min, max).execute();
}
- private void resetDialog()
+
+ protected void resetDialog()
{
exampleBtn.setEnabled(true);
textArea.setEnabled(true);
backBtn.setEnabled(parentSearchPanel != null);
}
- @Override
- public void run()
+ /**
+ * This asynchronous class allows for a single-threaded state machine
+ * SwingWorker to process a list of requests from multiple sources.
+ *
+ * A standard ProcessMonitor could be attached to this task, but it is
+ * currently not.
+ *
+ * @author hansonr
+ *
+ */
+ private class AsyncFetchTask extends AsyncSwingWorker
{
- boolean addToLast = false;
- List<String> aresultq = new ArrayList<>();
- List<String> presultTitle = new ArrayList<>();
- List<AlignmentI> presult = new ArrayList<>();
- List<AlignmentI> aresult = new ArrayList<>();
- List<DbSourceProxy> sources = sfetch
- .getSourceProxy((String) database.getSelectedItem());
- Iterator<DbSourceProxy> proxies = sources.iterator();
- String[] qries = textArea.getText().trim().split(";");
- List<String> nextFetch = Arrays.asList(qries);
- Iterator<String> en = Arrays.asList(new String[0]).iterator();
- int nqueries = qries.length;
-
- FeatureSettingsModelI preferredFeatureColours = null;
- while (proxies.hasNext() && (en.hasNext() || nextFetch.size() > 0))
- {
- if (!en.hasNext() && nextFetch.size() > 0)
- {
- en = nextFetch.iterator();
- nqueries = nextFetch.size();
- // save the remaining queries in the original array
- qries = nextFetch.toArray(new String[nqueries]);
- nextFetch = new ArrayList<>();
- }
- DbSourceProxy proxy = proxies.next();
- try
+ protected boolean addToLast = false;
+
+ protected List<String> aresultq = new ArrayList<>();
+
+ protected List<String> presultTitle = new ArrayList<>();
+
+ protected List<AlignmentI> presult = new ArrayList<>();
+
+ protected List<AlignmentI> aresult = new ArrayList<>();
+
+ protected FeatureSettingsModelI preferredFeatureColours = null;
+
+ protected List<DbSourceProxy> sources;
+
+ protected Iterator<DbSourceProxy> sourceIterator;
+
+ protected String[] fetchArray;
+
+ protected List<String> fetchList;
+
+ protected Iterator<String> fetchIterator;
+
+ protected int fetchCount;
+
+ protected DbSourceProxy source;
+
+ protected String ids;
+
+ protected long myID;
+
+ protected boolean isAsync;
+
+ protected Throwable taskError;
+
+ public AsyncFetchTask(String ids, Component owner, String title,
+ int delayMillis,
+ int min, int max)
+ {
+ super(owner, title, delayMillis, min, max);
+ this.ids = ids;
+ isAsync = (delayMillis != 0);
+ myID = Thread.currentThread().getId();
+ }
+
+ @Override
+ public void initAsync()
+ {
+ sources = jalview.ws.SequenceFetcher.getInstance()
+ .getSourceProxy((String) database.getSelectedItem());
+ sourceIterator = sources.iterator();
+ fetchArray = ids.trim().split(";");
+ fetchList = Arrays.asList(fetchArray);
+ }
+
+ private final static int STATE_INIT = 0;
+
+ private final static int STATE_NEXT_SOURCE = 10;
+
+ private final static int STATE_FETCH_SINGLE = 30;
+
+ private final static int STATE_FETCH_MULTIPLE = 40;
+
+ private final static int STATE_PROCESS = 50;
+
+ private final static int STATE_PARSE_RESULTS = 80;
+
+ private final static int STATE_DONE = 100;
+
+ protected static final int STATE_TASK_ERROR = 99;
+
+ @Override
+ public int doInBackgroundAsync(int progress)
+ {
+ System.out.println("SequenceFetcher.AsyncFetchTask " + isAsync + " "
+ + progress + " "
+ + taskError);
+ switch (progress)
{
- // update status
- guiWindow.setProgressBar(MessageManager.formatMessage(
+ case STATE_INIT:
+ case STATE_NEXT_SOURCE:
+ boolean doneFetching = (fetchIterator == null
+ || !fetchIterator.hasNext());
+ boolean havePending = (fetchList.size() > 0);
+ if (!sourceIterator.hasNext() || doneFetching && !havePending)
+ {
+ showProgress((presult.size() > 0)
+ ? MessageManager.getString("status.parsing_results")
+ : MessageManager.getString("status.processing"));
+ return STATE_PARSE_RESULTS;
+ }
+ source = sourceIterator.next();
+ if (doneFetching)
+ {
+ // if we are here, we must have some pending still
+ fetchCount = fetchList.size();
+ fetchIterator = fetchList.iterator();
+ // save the remaining queries in the original array
+ fetchArray = fetchList.toArray(new String[fetchCount]);
+ // and clear the
+ fetchList = new ArrayList<>();
+ }
+ showProgress(MessageManager.formatMessage(
"status.fetching_sequence_queries_from", new String[]
- { Integer.valueOf(nqueries).toString(),
- proxy.getDbName() }),
- Thread.currentThread().hashCode());
- if (proxy.getMaximumQueryCount() == 1)
+ { Integer.valueOf(fetchCount).toString(),
+ source.getDbName() }));
+ return (source.getMaximumQueryCount() == 1 ? STATE_FETCH_SINGLE
+ : STATE_FETCH_MULTIPLE);
+ case STATE_FETCH_SINGLE:
+ if (fetchIterator.hasNext())
{
- /*
- * proxy only handles one accession id at a time
- */
- while (en.hasNext())
+ return runSubtask(new Runnable()
{
- String acc = en.next();
- if (!fetchSingleAccession(proxy, acc, aresultq, aresult))
+
+ @Override
+ public void run()
{
- nextFetch.add(acc);
+ // source only handles one accession id at a time
+ try
+ {
+ if (!isAsync)
+ {
+ // for CrossRef2xmlTest only; to "allow the server to breathe"
+ // (but we are doing that already when isAsync is true)
+ Thread.sleep(5);
+ }
+ String notFound = fetchSingleAccession(source,
+ fetchIterator.next(), aresultq, aresult);
+ if (notFound != null)
+ {
+ fetchList.add(notFound);
+ }
+ } catch (Throwable e)
+ {
+ taskError = e;
+ }
}
- }
+ });
}
- else
+ return STATE_PROCESS;
+ case STATE_FETCH_MULTIPLE:
+ // proxy can fetch multiple accessions at one time
+ setProgressAsync(STATE_PROCESS);
+ return runSubtask(new Runnable()
{
- /*
- * proxy can fetch multiple accessions at one time
- */
- fetchMultipleAccessions(proxy, en, aresultq, aresult, nextFetch);
- }
- } catch (Exception e)
- {
- showErrorMessage("Error retrieving " + textArea.getText() + " from "
- + database.getSelectedItem());
- // error
- // +="Couldn't retrieve sequences from "+database.getSelectedItem();
- System.err.println("Retrieval failed for source ='"
- + database.getSelectedItem() + "' and query\n'"
- + textArea.getText() + "'\n");
- e.printStackTrace();
- } catch (OutOfMemoryError e)
- {
- showErrorMessage("Out of Memory when retrieving "
- + textArea.getText() + " from " + database.getSelectedItem()
- + "\nPlease see the Jalview FAQ for instructions for increasing the memory available to Jalview.\n");
- e.printStackTrace();
- } catch (Error e)
- {
- showErrorMessage("Serious Error retrieving " + textArea.getText()
- + " from " + database.getSelectedItem());
- e.printStackTrace();
- }
- // Stack results ready for opening in alignment windows
- if (aresult != null && aresult.size() > 0)
- {
- FeatureSettingsModelI proxyColourScheme = proxy
- .getFeatureColourScheme();
- if (proxyColourScheme != null)
- {
- preferredFeatureColours = proxyColourScheme;
- }
+ @Override
+ public void run()
+ {
+ try
+ {
+ fetchMultipleAccessions(source, fetchIterator, aresultq,
+ aresult, fetchList);
+ } catch (Throwable e)
+ {
+ taskError = e;
+ }
+ }
- AlignmentI ar = null;
- if (proxy.isAlignmentSource())
+ });
+ case STATE_PROCESS:
+ // Stack results ready for opening in alignment windows
+ if (aresult != null && aresult.size() > 0)
{
- addToLast = false;
- // new window for each result
- while (aresult.size() > 0)
+ FeatureSettingsModelI proxyColourScheme = source
+ .getFeatureColourScheme();
+ if (proxyColourScheme != null)
{
- presult.add(aresult.remove(0));
- presultTitle.add(
- aresultq.remove(0) + " " + getDefaultRetrievalTitle());
+ preferredFeatureColours = proxyColourScheme;
}
- }
- else
- {
- String titl = null;
- if (addToLast && presult.size() > 0)
+
+ AlignmentI ar = null;
+ if (source.isAlignmentSource())
{
- ar = presult.remove(presult.size() - 1);
- titl = presultTitle.remove(presultTitle.size() - 1);
+ addToLast = false;
+ // new window for each result
+ while (aresult.size() > 0)
+ {
+ presult.add(aresult.remove(0));
+ presultTitle.add(aresultq.remove(0) + " "
+ + getDefaultRetrievalTitle());
+ }
}
- // concatenate all results in one window
- while (aresult.size() > 0)
+ else
{
- if (ar == null)
+ String titl = null;
+ if (addToLast && presult.size() > 0)
{
- ar = aresult.remove(0);
+ ar = presult.remove(presult.size() - 1);
+ titl = presultTitle.remove(presultTitle.size() - 1);
}
- else
+ // concatenate all results in one window
+ while (aresult.size() > 0)
{
- ar.append(aresult.remove(0));
+ if (ar == null)
+ {
+ ar = aresult.remove(0);
+ }
+ else
+ {
+ ar.append(aresult.remove(0));
+ }
}
+ addToLast = true;
+ presult.add(ar);
+ presultTitle.add(titl);
}
- addToLast = true;
- presult.add(ar);
- presultTitle.add(titl);
}
+ showProgress(MessageManager.getString("status.finshed_querying"));
+ return STATE_NEXT_SOURCE;
+ case STATE_PARSE_RESULTS:
+ while (presult.size() > 0)
+ {
+ parseResult(presult.remove(0), presultTitle.remove(0), null,
+ preferredFeatureColours);
+ }
+ break;
+ case STATE_TASK_ERROR:
+ showProgress(null);
+ super.cancelAsync();
+ return 0; // arbitrary; ignored
}
- guiWindow.setProgressBar(
- MessageManager.getString("status.finshed_querying"),
- Thread.currentThread().hashCode());
+ return STATE_DONE;
}
- guiWindow
- .setProgressBar(
- (presult.size() > 0)
- ? MessageManager
- .getString("status.parsing_results")
- : MessageManager.getString("status.processing"),
- Thread.currentThread().hashCode());
- // process results
- while (presult.size() > 0)
- {
- parseResult(presult.remove(0), presultTitle.remove(0), null,
- preferredFeatureColours);
+
+ protected void resume()
+ {
+ if (!isAsync)
+ {
+ return;
+ }
+ super.setPaused(false);
+ super.stateLoop();
}
- // only remove visual delay after we finished parsing.
- guiWindow.setProgressBar(null, Thread.currentThread().hashCode());
- if (nextFetch.size() > 0)
- {
- StringBuffer sb = new StringBuffer();
- sb.append("Didn't retrieve the following "
- + (nextFetch.size() == 1 ? "query"
- : nextFetch.size() + " queries")
- + ": \n");
- int l = sb.length(), lr = 0;
- for (String s : nextFetch)
+
+ private int runSubtask(Runnable subtask)
+ {
+
+ Runnable r = new Runnable()
{
- if (l != sb.length())
+
+ @Override
+ public void run()
{
- sb.append("; ");
+ try
+ {
+ subtask.run();
+ } catch (Throwable e)
+ {
+ taskError = e;
+ }
+ if (checkError(taskError))
+ {
+ setProgressAsync(STATE_TASK_ERROR);
+ }
+ else
+ {
+ taskError = null;
+ }
+ if (isAsync)
+ {
+ resume();
+ }
}
- if (lr - sb.length() > 40)
+ };
+
+ if (isAsync)
+ {
+ super.setPaused(true);
+ new Thread(r).start();
+ return 0; // arbitrary return -- ignored because we are paused.
+ }
+ r.run();
+ return super.getProgressAsync();
+ }
+
+ @Override
+ public void doneAsync()
+ {
+ showProgress(null);
+ if (fetchList.size() > 0)
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("Didn't retrieve the following "
+ + (fetchList.size() == 1 ? "query"
+ : fetchList.size() + " queries")
+ + ": \n");
+ int l = sb.length(), lr = 0;
+ for (String s : fetchList)
{
- sb.append("\n");
+ if (l != sb.length())
+ {
+ sb.append("; ");
+ }
+ if (lr - sb.length() > 40)
+ {
+ sb.append("\n");
+ }
+ sb.append(s);
}
- sb.append(s);
+ showErrorMessage(sb.toString());
}
- showErrorMessage(sb.toString());
+ resetDialog();
}
- resetDialog();
+
+ /**
+ *
+ * @param e
+ * @return true if there was an error and we need to STOP
+ */
+ protected boolean checkError(Throwable e)
+ {
+ if (e == null)
+ {
+ return false;
+ }
+ String problem = "retrieving " + ids + " from "
+ + database.getSelectedItem();
+ if (e instanceof Exception)
+ {
+ showErrorMessage("Error " + problem);
+ System.err.println("Retrieval failed for source ='"
+ + database.getSelectedItem() + "' and query\n'"
+ + ids + "'\n");
+ }
+ else if (e instanceof OutOfMemoryError)
+ {
+ showErrorMessage("Out of Memory when " + problem
+ + "\nPlease see the Jalview FAQ for instructions for increasing the memory available to Jalview.\n");
+ // option here to return false and quit this, but that is not how
+ // original code works - BH
+ // return false;
+ }
+ else
+ {
+ showErrorMessage("Serious Error " + problem);
+ }
+ e.printStackTrace();
+ return true;
+ }
+
+ private void showProgress(String msg)
+ {
+ guiWindow.setProgressBar(msg, myID);
+ }
+
}
/**
* a list of successful queries to add to
* @param aresult
* a list of retrieved alignments to add to
- * @return true if the fetch was successful, else false
+ * @return null if the fetch was successful; the accession if not
*/
- boolean fetchSingleAccession(DbSourceProxy proxy, String accession,
- List<String> aresultq, List<AlignmentI> aresult)
+ String fetchSingleAccession(DbSourceProxy proxy, String accession,
+ List<String> aresultq, List<AlignmentI> aresult) throws Throwable
{
- boolean success = false;
- try
- {
- if (aresult != null)
- {
- try
- {
- // give the server a chance to breathe
- Thread.sleep(5);
- } catch (Exception e)
- {
- //
- }
- }
-
- AlignmentI indres = null;
- try
- {
- indres = proxy.getSequenceRecords(accession);
- } catch (OutOfMemoryError oome)
- {
- new OOMWarning(
- "fetching " + accession + " from " + proxy.getDbName(),
- oome, this);
- }
- if (indres != null)
- {
- aresultq.add(accession);
- aresult.add(indres);
- success = true;
- }
- } catch (Exception e)
+ AlignmentI indres = proxy.getSequenceRecords(accession);
+ if (indres == null)
{
- Cache.log.info("Error retrieving " + accession + " from "
- + proxy.getDbName(), e);
+ return accession;
}
- return success;
+ aresultq.add(accession);
+ aresult.add(indres);
+ return null;
}
/**
for (String q : queries)
{
- // BH 2019.01.25 dbr is never used.
-// DBRefEntry dbr = new DBRefEntry();
-// dbr.setSource(proxy.getDbSource());
-// dbr.setVersion(null);
+ // BH 2019.01.25 dbr is never used.
+ // DBRefEntry dbr = new DBRefEntry();
+ // dbr.setSource(proxy.getDbSource());
+ // dbr.setVersion(null);
String accId = proxy.getAccessionIdFromQuery(q);
-// dbr.setAccessionId(accId);
+ // dbr.setAccessionId(accId);
boolean rfound = false;
for (int r = 0, nr = rs.length; r < nr; r++)
{
{
af.getViewport().applyFeaturesStyle(preferredFeatureColours);
}
- if (Cache.getDefault("HIDE_INTRONS", true))
+ if (Cache.getDefault(Preferences.HIDE_INTRONS, true))
{
af.hideFeatureColumns(SequenceOntologyI.EXON, false);
}
try
{
- af.setMaximum(Cache.getDefault("SHOW_FULLSCREEN", false));
+ af.setMaximum(
+ Cache.getDefault(Preferences.SHOW_FULLSCREEN, false));
} catch (Exception ex)
{
}
@Override
public void run()
{
- JvOptionPane.showInternalMessageDialog(Desktop.desktop, error,
+ JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
+ error,
MessageManager.getString("label.error_retrieving_data"),
JvOptionPane.WARNING_MESSAGE);
}
frame.setVisible(false);
}
- public void setQuery(String ids)
- {
- textArea.setText(ids);
- }
-
/**
* Called to modify the search panel for embedding as an alternative tab of a
* free text search panel. The database choice list is hidden (since the
backBtn.setVisible(true);
parentSearchPanel = parentPanel;
}
+
+
}