package name.abuchen.portfolio.ui.wizards.security; import java.text.MessageFormat; import java.time.LocalDate; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.databinding.beans.BeanProperties; import org.eclipse.core.databinding.observable.value.IObservableValue; import org.eclipse.core.databinding.validation.MultiValidator; import org.eclipse.core.databinding.validation.ValidationStatus; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.layout.TableColumnLayout; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import name.abuchen.portfolio.model.Exchange; import name.abuchen.portfolio.model.LatestSecurityPrice; import name.abuchen.portfolio.model.Security; import name.abuchen.portfolio.online.Factory; import name.abuchen.portfolio.online.QuoteFeed; import name.abuchen.portfolio.ui.Messages; import name.abuchen.portfolio.ui.PortfolioPlugin; import name.abuchen.portfolio.ui.util.BindingHelper; import name.abuchen.portfolio.ui.util.QuotesTableViewer; public class HistoricalQuoteProviderPage extends AbstractQuoteProviderPage { private QuotesTableViewer tableSampleData; private Map<Object, List<LatestSecurityPrice>> cacheQuotes = new HashMap<Object, List<LatestSecurityPrice>>(); // read and write 'currentJob' only from the UI thread; used to check // whether a more recent job has already been started private LoadHistoricalQuotes currentJob; public HistoricalQuoteProviderPage(final EditSecurityModel model, BindingHelper bindings) { super(model); setTitle(Messages.EditWizardQuoteFeedTitle); // validate that quote provider message is null -> no errors bindings.getBindingContext().addValidationStatusProvider(new MultiValidator() { IObservableValue observable = BeanProperties.value("statusHistoricalQuotesProvider").observe(model); //$NON-NLS-1$ @Override protected IStatus validate() { return observable.getValue() == null ? ValidationStatus.ok() : ValidationStatus.error(observable.getValue().toString()); } }); } @Override protected String getFeed() { return getModel().getFeed(); } @Override protected void setFeed(String feed) { getModel().setFeed(feed); } @Override protected String getFeedURL() { return getModel().getFeedURL(); } @Override protected void setFeedURL(String feedURL) { getModel().setFeedURL(feedURL); } @Override protected void setStatus(String status) { getModel().setStatusHistoricalQuotesProvider(status); } @Override protected List<QuoteFeed> getAvailableFeeds() { return Factory.getQuoteFeedProvider(); } @Override protected QuoteFeed getQuoteFeedProvider(String feedId) { return Factory.getQuoteFeedProvider(feedId); } @Override protected void createSampleArea(Composite container) { Composite composite = new Composite(container, SWT.NONE); TableColumnLayout layout = new TableColumnLayout(); composite.setLayout(layout); tableSampleData = new QuotesTableViewer(composite); } @Override protected void reinitCaches() { cacheQuotes = new HashMap<Object, List<LatestSecurityPrice>>(); } @Override protected void clearSampleQuotes() { currentJob = null; tableSampleData.setInput(null); tableSampleData.refresh(); } @Override protected void showSampleQuotes(QuoteFeed feed, Exchange exchange) { Object cacheKey = exchange != null ? exchange : getModel().getFeedURL(); List<LatestSecurityPrice> quotes = cacheQuotes.get(cacheKey); if (quotes != null) { tableSampleData.setInput(quotes); tableSampleData.refresh(); } else { tableSampleData.setMessage(Messages.EditWizardQuoteFeedMsgLoading); tableSampleData.refresh(); Job job = new LoadHistoricalQuotes(feed, exchange); job.setUser(true); job.schedule(150); } } private class LoadHistoricalQuotes extends Job { private QuoteFeed feed; private Exchange exchange; public LoadHistoricalQuotes(QuoteFeed feed, Exchange exchange) { super(MessageFormat.format(Messages.JobMsgSamplingHistoricalQuotes, exchange != null ? exchange.getName() : "")); //$NON-NLS-1$ this.feed = feed; this.exchange = exchange; HistoricalQuoteProviderPage.this.currentJob = this; } @Override protected IStatus run(IProgressMonitor monitor) { try { Security s = buildTemporarySecurity(); if (exchange != null) s.setTickerSymbol(exchange.getId()); s.setFeed(feed.getId()); // last 2 months as sample LocalDate t = LocalDate.now().minusMonths(2); final List<LatestSecurityPrice> quotes = feed.getHistoricalQuotes(s, t, new ArrayList<Exception>()); Display.getDefault().asyncExec(() -> { if (LoadHistoricalQuotes.this.equals(HistoricalQuoteProviderPage.this.currentJob) && !tableSampleData.getControl().isDisposed()) { HistoricalQuoteProviderPage.this.currentJob = null; cacheQuotes.put(exchange, quotes); if (!tableSampleData.getControl().isDisposed()) { tableSampleData.setInput(quotes); tableSampleData.refresh(); } } }); } catch (Exception e) { Display.getDefault().asyncExec(() -> { if (LoadHistoricalQuotes.this.equals(HistoricalQuoteProviderPage.this.currentJob) && !tableSampleData.getControl().isDisposed()) { currentJob = null; tableSampleData.setMessage(Messages.EditWizardQuoteFeedMsgErrorOrNoData); tableSampleData.refresh(); } }); PortfolioPlugin.log(e); } return Status.OK_STATUS; } } }