package name.abuchen.portfolio.ui.wizards.security; import static name.abuchen.portfolio.ui.util.SWTHelper.dateWidth; import static name.abuchen.portfolio.ui.util.SWTHelper.placeBelow; import static name.abuchen.portfolio.ui.util.SWTHelper.widestWidget; import java.text.MessageFormat; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; 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.swt.SWT; import org.eclipse.swt.layout.FormAttachment; import org.eclipse.swt.layout.FormData; import org.eclipse.swt.layout.FormLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import name.abuchen.portfolio.model.Exchange; import name.abuchen.portfolio.model.LatestSecurityPrice; import name.abuchen.portfolio.model.Security; import name.abuchen.portfolio.money.Values; 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; public class LatestQuoteProviderPage extends AbstractQuoteProviderPage { private static class DummyQuoteFeed implements QuoteFeed { @Override public String getId() { return null; } @Override public String getName() { return Messages.EditWizardOptionSameAsHistoricalQuoteFeed; } @Override public boolean updateLatestQuotes(List<Security> securities, List<Exception> errors) { return false; } @Override public boolean updateHistoricalQuotes(Security security, List<Exception> errors) { return false; } @Override public List<LatestSecurityPrice> getHistoricalQuotes(Security security, LocalDate start, List<Exception> errors) { return null; } @Override public List<LatestSecurityPrice> getHistoricalQuotes(String response, List<Exception> errors) { return null; } @Override public List<Exchange> getExchanges(Security subject, List<Exception> errors) { return null; } } private class LoadLatestQuote extends Job { private QuoteFeed feed; private Exchange exchange; public LoadLatestQuote(QuoteFeed feed, Exchange exchange) { super(MessageFormat.format(Messages.JobMsgSamplingHistoricalQuotes, exchange != null ? exchange.getName() : "")); //$NON-NLS-1$ this.feed = feed; this.exchange = exchange; } @Override protected IStatus run(IProgressMonitor monitor) { try { final Security s = buildTemporarySecurity(); // exchange is not bound to model (only set in #afterPage) // therefore we must set it explicitly here if (exchange != null) s.setTickerSymbol(exchange.getId()); s.setFeed(feed.getId()); List<Security> list = new ArrayList<>(); list.add(s); feed.updateLatestQuotes(list, new ArrayList<Exception>()); Display.getDefault().asyncExec(() -> { if (valueLatestPrices == null || valueLatestPrices.isDisposed()) return; if (s.getLatest() != null) { LatestSecurityPrice p = s.getLatest(); valueLatestPrices.setText(Values.Quote.format(p.getValue())); valueLatestTrade.setText(Values.Date.format(p.getTime())); long daysHigh = p.getHigh(); valueDaysHigh.setText( daysHigh == -1 ? Messages.LabelNotAvailable : Values.Quote.format(daysHigh)); long daysLow = p.getLow(); valueDaysLow.setText(daysLow == -1 ? Messages.LabelNotAvailable : Values.Quote.format(daysLow)); long volume = p.getVolume(); valueVolume.setText(volume == -1 ? Messages.LabelNotAvailable : String.format("%,d", volume)); //$NON-NLS-1$ long prevClose = p.getPreviousClose(); valuePreviousClose.setText( prevClose == -1 ? Messages.LabelNotAvailable : Values.Quote.format(prevClose)); } else { clearSampleQuotes(); } }); } catch (Exception e) { Display.getDefault().asyncExec(() -> clearSampleQuotes()); PortfolioPlugin.log(e); } return Status.OK_STATUS; } } private static final String EMPTY_LABEL = ""; //$NON-NLS-1$ private static final QuoteFeed EMTPY_QUOTE_FEED = new DummyQuoteFeed(); private Label valueLatestPrices; private Label valueLatestTrade; private Label valueDaysHigh; private Label valueDaysLow; private Label valueVolume; private Label valuePreviousClose; public LatestQuoteProviderPage(final EditSecurityModel model, BindingHelper bindings) { super(model); setTitle(Messages.EditWizardLatestQuoteFeedTitle); // validate that quote provider message is null -> no errors bindings.getBindingContext().addValidationStatusProvider(new MultiValidator() { IObservableValue observable = BeanProperties.value("statusLatestQuotesProvider").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().getLatestFeed(); } @Override protected void setFeed(String feed) { getModel().setLatestFeed(feed); } @Override protected String getFeedURL() { return getModel().getLatestFeedURL(); } @Override protected void setFeedURL(String feedURL) { getModel().setLatestFeedURL(feedURL); } @Override protected void setStatus(String status) { getModel().setStatusLatestQuotesProvider(status); } @Override protected List<QuoteFeed> getAvailableFeeds() { List<QuoteFeed> feeds = new ArrayList<>(); feeds.add(EMTPY_QUOTE_FEED); for (QuoteFeed feed : Factory.getQuoteFeedProvider()) { // do not include adjusted close (the difference between close and // adjusted close are only relevant for historical quotes) if (feed.getId().equals("YAHOO-ADJUSTEDCLOSE")) //$NON-NLS-1$ continue; feeds.add(feed); } return feeds; } @Override protected QuoteFeed getQuoteFeedProvider(String feedId) { if (feedId == null) return EMTPY_QUOTE_FEED; return Factory.getQuoteFeedProvider(feedId); } @Override protected void createSampleArea(Composite container) { Composite composite = new Composite(container, SWT.NONE); Label labelLatestPrice = new Label(composite, SWT.NONE); labelLatestPrice.setText(Messages.ColumnLatestPrice); valueLatestPrices = new Label(composite, SWT.RIGHT); Label labelLatestTrade = new Label(composite, SWT.NONE); labelLatestTrade.setText(Messages.ColumnLatestTrade); valueLatestTrade = new Label(composite, SWT.RIGHT); Label labelDaysHigh = new Label(composite, SWT.NONE); labelDaysHigh.setText(Messages.ColumnDaysHigh); valueDaysHigh = new Label(composite, SWT.RIGHT); Label labelDaysLow = new Label(composite, SWT.NONE); labelDaysLow.setText(Messages.ColumnDaysLow); valueDaysLow = new Label(composite, SWT.RIGHT); Label labelVolume = new Label(composite, SWT.NONE); labelVolume.setText(Messages.ColumnVolume); valueVolume = new Label(composite, SWT.RIGHT); Label labelPreviousClose = new Label(composite, SWT.NONE); labelPreviousClose.setText(Messages.ColumnPreviousClose); valuePreviousClose = new Label(composite, SWT.RIGHT); // layout FormLayout layout = new FormLayout(); layout.marginLeft = 5; layout.marginRight = 5; composite.setLayout(layout); Control biggest = widestWidget(labelLatestPrice, labelLatestTrade, labelDaysHigh, labelDaysLow, labelVolume, labelPreviousClose); int width = dateWidth(composite); FormData data = new FormData(); data.top = new FormAttachment(valueLatestPrices, 0, SWT.CENTER); labelLatestPrice.setLayoutData(data); data = new FormData(); data.top = new FormAttachment(0, 5); data.left = new FormAttachment(biggest, 5); data.width = width; valueLatestPrices.setLayoutData(data); placeBelow(valueLatestPrices, labelLatestTrade, valueLatestTrade); placeBelow(valueLatestTrade, labelDaysHigh, valueDaysHigh); placeBelow(valueDaysHigh, labelDaysLow, valueDaysLow); placeBelow(valueDaysLow, labelVolume, valueVolume); placeBelow(valueVolume, labelPreviousClose, valuePreviousClose); } @Override protected void reinitCaches() { // no caches } @Override protected void clearSampleQuotes() { valueLatestPrices.setText(EMPTY_LABEL); valueLatestTrade.setText(EMPTY_LABEL); valueDaysHigh.setText(EMPTY_LABEL); valueDaysLow.setText(EMPTY_LABEL); valueVolume.setText(EMPTY_LABEL); valuePreviousClose.setText(EMPTY_LABEL); } @Override protected void showSampleQuotes(QuoteFeed feed, Exchange exchange) { new LoadLatestQuote(feed, exchange).schedule(); } }